【莫比乌斯反演】BZOJ2005 [NOI2010]能量采集
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分开考虑贡献真是神奇,不过对于现在是再自然不过的想法了。
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #define ll long long 5 using namespace std; 6 const int maxn=1e5+5; 7 8 int flag[maxn],prime[maxn],cnt; 9 int mu[maxn]; 10 int N,M; 11 12 int getmu(){ 13 mu[1]=1; 14 for(int i=2;i<=N;i++){ 15 if(!flag[i]){ 16 mu[i]=-1; 17 prime[++cnt]=i; 18 } 19 for(int j=1;i*prime[j]<=N&&j<=cnt;j++){ 20 flag[i*prime[j]]=1; 21 if(i%prime[j]==0){ 22 mu[i*prime[j]]=0; 23 break; 24 } 25 mu[i*prime[j]]=-mu[i]; 26 } 27 } 28 } 29 30 ll work(int x){ 31 ll ret=0; 32 int n=N/x,m=M/x; 33 for(int i=1;i<=n;i++) 34 ret+=1ll*mu[i]*(n/i)*(m/i); 35 return ret; 36 } 37 38 int main(){ 39 scanf("%d%d",&N,&M); 40 if(N>M) swap(N,M); 41 getmu(); 42 43 ll ans=0; 44 for(int i=1;i<=N;i++) 45 ans+=work(i)*(2*i-1); 46 printf("%lld\n",ans); 47 return 0; 48 }