莫比乌斯反演 刷题记录
BZOJ1101: [POI2007]Zap
- 题意:对于给定的整数a,b和d,求有多少正整数对x,y,满足x<=a,y<=b,并且gcd(x,y)=d
- 直接求有多少正整数对x,y,gcd(x,y)=d比较难求,而且询问有50000个,
\begin{gather*}
要求\ \ \ \ \ \ \ \ f( n) =\ \sum\nolimits ^{\ b}_{\ y=1} \ \ \sum\nolimits ^{\ a}_{\ x=1} \ [ \ gcd( x,y) =n\ ]\\
\\
则\ \ \ \ \ \ \ \ \ \ \ F( n) =\sum\nolimits ^{\ b}_{\ y=1}\sum\nolimits ^{\ a}_{\ x=1}\sum\nolimits _{\ n|gcd( x,y)} \ 1\\
\\
因为\ \ \ \ \ \ \ \sum\nolimits _{\ n|gcd( x,y)} 1=\sum\nolimits _{\ n|d} \ [ \ gcd( x,y) =d\ ]\\
\\
F( n) \ =\ \sum\nolimits _{\ n|d} \ \ f( d)\\
\\
然后就是莫比乌斯反演辣\\
\\
\end{gather*}- 代码:
-
1 #include <bits/stdc++.h> 2 #define nmax 50010 3 4 using namespace std; 5 int pri[nmax],table[nmax],mu[nmax]; 6 int cp=-1; 7 8 void getmu(){ 9 for (int i=2; i<nmax; i++) { 10 if(table[i]==0) { pri[++cp]=i; mu[i]=-1; } 11 for (int j=0; j<=cp; j++) { 12 int t=i*pri[j]; 13 if(t>=nmax) break; 14 table[t]=1; 15 if(i%pri[j]==0) {mu[t]=0; break;} 16 mu[t]=mu[i]*(-1); 17 } 18 } 19 mu[1]=1; 20 for (int i=0; i<nmax; i++) mu[i]+=mu[i-1]; 21 } 22 23 int main(){ 24 int n,a,b,d; 25 getmu(); 26 cin>>n; 27 while(n--){ 28 scanf("%d%d%d",&a,&b,&d); 29 if(a>b) swap(a,b); 30 a/=d; 31 b/=d; 32 //分块算答案 33 int last,ans=0; 34 for (int i=1; i<=a; i=last+1){ 35 last=min( a/(a/i) , b/(b/i) ); 36 ans+=(a/i)*(b/i)*(mu[last]-mu[i-1]); 37 } 38 printf("%d\n",ans); 39 } 40 return 0; 41 }
POJ3904 Sky Code
- 给出一堆数,让你找有多少个四元组(a,b,c,d),满足a,b,c,d的gcd为1
- 比较裸的莫比乌斯反演,跟上面一样
- F(n) 为满足n|gcd(a,b,c,d)的四元组个数,f(n)为满足gcd(a,b,c,d)=1的四元组个数
- 于是这样
\begin{equation*}
F( n) =\sum\limits _{n|d} f( d)
\end{equation*} - 于是就可以根据公式反演了
- 代码:
1 #include <algorithm> 2 #include <cstring> 3 #include <iostream> 4 #include <cstdio> 5 #define nmax 10010 6 7 using namespace std; 8 typedef long long ll; 9 int n,minx,cp=0; 10 int t1[nmax]={0},in[nmax],mu[nmax],t3[nmax],pri[nmax]; 11 ll c[nmax][5]={0}; 12 ll ans; 13 14 void getmu(){ 15 for (int i=2; i<nmax; i++) { 16 if(t3[i]==0) { pri[++cp]=i; mu[i]=-1; } 17 for (int j=1; j<=cp; j++) { 18 int tx=i*pri[j]; 19 if(tx>nmax) break; 20 t3[tx]=1; 21 if(i%pri[j]==0) break; 22 mu[tx]=mu[i]*(-1); 23 } 24 } 25 mu[1]=1; 26 } 27 28 int main(){ 29 //组合数的表 c 30 for (ll i=1; i<nmax; i++) c[i][1]=i; 31 for (ll i=2; i<=4; i++) c[i][i]=1; 32 for (ll j=2; j<=4; j++) for (ll i=j+1; i<nmax; i++) c[i][j]=c[i-1][j]+c[i-1][j-1]; 33 //莫比乌斯函数的表 mu 34 getmu(); 35 while(scanf("%d",&n)!=EOF){ 36 memset(t1,0,sizeof(t1)); 37 ans=0; 38 minx=nmax*10; 39 for (int i=0; i<n; i++) { scanf("%d",&in[i]); minx=min(minx,in[i]); } 40 //t1[i]有多少个数是i的倍数 41 for (int i=0; i<n; i++) for (int j=1; j*j<=in[i]; j++) { 42 if(in[i]%j==0) { 43 t1[j]++; 44 if( j != in[i]/j ) t1[in[i]/j]++; 45 } 46 } 47 48 //反演部分 49 for (int i=1; i<nmax; i++) ans+=mu[i]*c[ t1[i] ][4]; 50 printf("%lld\n",ans); 51 } 52 return 0; 53 }