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

[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,所以我们要求的是

2(\sum_{i=1}^{n}\sum_{j=1}^{m}gcd(i,j)) - n\cdot m

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

\sum_{i=1}^{n}\sum_{j=1}^{m}gcd(i,j)=\sum_{d=1}^{n}d\sum_{i=1}^{n}\sum_{j=1}^{m}((bool)gcd(i,j)==d)

我们设

f(k)=\sum_{i=1}^{n}\sum_{j=1}^{m}(gcd(i,j)==k)

我们先求一个更简单的,

F(k)=\sum_{k|d}f(d)\;,\;F(k)=\sum_{i=1}^{n}\sum_{j=1}^{m}(k|gcd(i,j))=\lfloor \frac{n}{k}\rfloor\lfloor \frac{m}{k}\rfloor

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

f(k)=\sum_{k|d}^{n}F(d)\mu (\frac{d}{k})=\sum_{k|d}^{n}\lfloor \frac{n}{d}\rfloor\lfloor \frac{m}{d}\rfloor\mu (\frac{d}{k})=\sum_{d=1}^{\lfloor \frac{n}{k}\rfloor}\mu(d) \lfloor \frac{n}{dk}\rfloor\lfloor \frac{m}{dk}\rfloor

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

\sum_{k=1}^{n}k\sum_{d=1}^{\lfloor \frac{n}{k}\rfloor}\lfloor \frac{n}{dk}\rfloor\lfloor \frac{m}{dk}\rfloor\mu (d)

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

\sum_{T=1}^{n}\lfloor \frac{n}{T}\rfloor\lfloor \frac{n}{T}\rfloor\sum_{k|T}k\cdot \mu(\frac{T}{k})

仔细一看,后面的

\sum_{k|T}k\cdot \mu(\frac{T}{k})

大有文章。

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

G(n)=n\;,\;g(n)=\phi (n),那么不就有这个关系

n=\sum_{d|n}\phi (d)\Rightarrow G(n)=\sum_{d|n}g(d)

反演过来,刚好是

\sum_{d|n}n\cdot \mu(\frac{n}{d})=\phi (n)

所以最终的答案就是

2(\sum_{T=1}^{n}\lfloor \frac{n}{T}\rfloor\lfloor \frac{n}{T}\rfloor\phi (T)) - n\cdot m

用欧拉筛做一遍,然后枚举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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章