[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;
}

 

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