![【莫比乌斯反演】BZOJ2005 [NOI2010]能量采集 【莫比乌斯反演】BZOJ2005 [NOI2010]能量采集](https://image.shishitao.com:8440/aHR0cHM6Ly9ia3FzaW1nLmlrYWZhbi5jb20vdXBsb2FkL2NoYXRncHQtcy5wbmc%2FIQ%3D%3D.png?!?w=700&webp=1)
Description
求sigma gcd(x,y)*2-1,1<=x<=n, 1<=y<=m。n, m<=1e5。
Solution
f(n)为gcd正好是n的(x,y)的个数
F(n)为gcd是n的倍数的(x,y)的个数
我们要求的就是f(i)
然而这个不好直接算,可F(i)可以直接用(n/i)*(m/i)得到
那么有F(n)=sigma n|i f(i)
于是有f(n)=sigma n|i mu(i)*F(i)
这就是莫比乌斯反演,不过这道题直接用容斥的思想想也很容易得到上面那个式子
那么考虑每一个gcd的贡献
把n和m除以gcd后,就相当于要求n次f(1)
每次均摊logn
Code
也有不用反演的做法,大概是从后往前算,每一步都严格定义,用容斥做。
这道题是我做的BZOJ第三题,不过当时只会80/90暴力然后去看的题解的容斥,那时候觉得把每一个gcd分开考虑贡献真是神奇,不过对于现在是再自然不过的想法了。
#include<cstdio>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
const int maxn=1e5+; int flag[maxn],prime[maxn],cnt;
int mu[maxn];
int N,M; int getmu(){
mu[]=;
for(int i=;i<=N;i++){
if(!flag[i]){
mu[i]=-;
prime[++cnt]=i;
}
for(int j=;i*prime[j]<=N&&j<=cnt;j++){
flag[i*prime[j]]=;
if(i%prime[j]==){
mu[i*prime[j]]=;
break;
}
mu[i*prime[j]]=-mu[i];
}
}
} ll work(int x){
ll ret=;
int n=N/x,m=M/x;
for(int i=;i<=n;i++)
ret+=1ll*mu[i]*(n/i)*(m/i);
return ret;
} int main(){
scanf("%d%d",&N,&M);
if(N>M) swap(N,M);
getmu(); ll ans=;
for(int i=;i<=N;i++)
ans+=work(i)*(*i-);
printf("%lld\n",ans);
return ;
}