[Noi2010]能量采集 (莫比乌斯反演)

2022-10-15,,,

[Noi2010]能量采集

Description

栋栋有一块长方形的地,他在地上种了一种能量植物,这种植物可以采集太阳光的能量。在这些植物采集能量后,

栋栋再使用一个能量汇集机器把这些植物采集到的能量汇集到一起。 栋栋的植物种得非常整齐,一共有n列,每列

有m棵,植物的横竖间距都一样,因此对于每一棵植物,栋栋可以用一个坐标(x, y)来表示,其中x的范围是1至n,

表示是在第x列,y的范围是1至m,表示是在第x列的第y棵。 由于能量汇集机器较大,不便移动,栋栋将它放在了

一个角上,坐标正好是(0, 0)。 能量汇集机器在汇集的过程中有一定的能量损失。如果一棵植物与能量汇集机器

连接而成的线段上有k棵植物,则能量的损失为2k + 1。例如,当能量汇集机器收集坐标为(2, 4)的植物时,由于

连接线段上存在一棵植物(1, 2),会产生3的能量损失。注意,如果一棵植物与能量汇集机器连接的线段上没有植

物,则能量损失为1。现在要计算总的能量损失。 下面给出了一个能量采集的例子,其中n = 5,m = 4,一共有20

棵植物,在每棵植物上标明了能量汇集机器收集它的能量时产生的能量损失。 在这个例子中,总共产生了36的能

量损失。

Input

仅包含一行,为两个整数n和m。

Output

仅包含一个整数,表示总共产生的能量损失。

Sample Input

【样例输入1】

5 4

【样例输入2】

3 4

Sample Output

【样例输出1】

36

【样例输出2】

20

对于100%的数据:1 ≤ n, m ≤ 100,000。

题解

莫比乌斯反演

老套路,先设n<m

我们注意到每个格子的贡献其实就是2 * gcd(x,y) - 1,所以我们要求的是

其中最难蒜的就是中间那一坨

我们设

我们先求一个更简单的,

那么根据莫比乌斯反演公式,

然后把f(k)代入前面的那一坨式子里

因为dk <= n,所以我们不妨就设T=dk ,枚举 T,然后枚举 T 的因数 k

仔细一看,后面的

大有文章。

这不就是莫比乌斯反演的后半段吗?我们解一解,设

,那么不就有这个关系

反演过来,刚好是

所以最终的答案就是

用欧拉筛做一遍,然后枚举van事。

CODE

之前欧拉筛里写了μ,懒得删了

#include<cstdio>
#include<cstring>
#include<vector>
#include<stack>
#include<queue>
#include<algorithm>
#include<map>
#include<cmath>
#include<iostream>
#define MAXN 100005
#define LL long long
#define rg register
#define lowbit(x) (-(x) & (x))
#define ENDL putchar('\n')
#pragma GCC optimize(2)
//#pragma G++ optimize(3)
//#define int LL
using namespace std;
inline int read() {
int f = 1,x = 0;char s = getchar();
while(s < '0' || s > '9') {if(s == '-')f = -1;s = getchar();}
while(s >= '0' && s <= '9') {x = x * 10 - '0' + s;s = getchar();}
return x * f;
}
LL zxy;
LL n,m,i,j,k,s,o;
int p[MAXN],cnt;
int mu[MAXN];
LL phi[MAXN];
bool f[MAXN];
inline void sieve(int n) {
mu[1] = 1;
phi[1] = 1;
for(rg int i = 2;i <= n;i ++) {
if(!f[i]) {
p[++ cnt] = i;
mu[i] = -1;
phi[i] = i-1;
}
for(rg int j = 1;j <= cnt && i * p[j] <= n;j ++) {
f[i * p[j]] = 1;
if(i % p[j] == 0) {
mu[i * p[j]] = 0;
phi[i * p[j]] = p[j] * phi[i];
break;
}
else mu[i * p[j]] = -mu[i],phi[i * p[j]] = phi[i] * phi[p[j]];
}
}
return ;
}
signed main() {
sieve(100000);
n = read();m = read();
if(n > m) swap(n,m);
LL ans = 0;
for(int i = 1;i <= n;i ++) {
ans += (n/i) *1ll* (m/i) * phi[i];
}
printf("%lld\n",2ll * ans - n * 1ll * m);
return 0;
}

[Noi2010]能量采集 (莫比乌斯反演)的相关教程结束。

《[Noi2010]能量采集 (莫比乌斯反演).doc》

下载本文的Word格式文档,以方便收藏与打印。