BZOJ1101: [POI2007]Zap(莫比乌斯反演)
Description
FGD正在破解一段密码,他需要回答很多类似的问题:对于给定的整数a,b和d,有多少正整数对x,y,满足x<=a
,y<=b,并且gcd(x,y)=d。作为FGD的同学,FGD希望得到你的帮助。
Input
第一行包含一个正整数n,表示一共有n组询问。(1<=n<= 50000)接下来n行,每行表示一个询问,每行三个
正整数,分别为a,b,d。(1<=d<=a,b<=50000)
Output
对于每组询问,输出到输出文件zap.out一个正整数,表示满足条件的整数对数。
Sample Input
4 5 2
6 4 3
Sample Output
2
解题思路:
${\sum_{i=1}^{n}}{\sum_{j=1}^{m}}gcd(i,j)==d$
$={\sum_{d|i}^{n}}{\sum_{d|j}^{m}}\varepsilon(gcd(\frac{i}{d},\frac{j}{d}))$
设${M={\left \lfloor {\frac{m}{d}} \right \rfloor}},{N={\left \lfloor {\frac{n}{d}} \right \rfloor}}$
$原式={\sum_{i=1}^{N}}{\sum_{j=1}^{M}}\varepsilon(gcd(i,j))$
$={\sum_{i=1}^{N}}{\sum_{j=1}^{M}}{\sum_{d|gcd(i,j)}}{\mu (d)}$
$={\sum_{d=1}^{min(N,M)}}{\sum_{d|i}^{N}}{\sum_{d|j}^{M}}{\mu(d)}$
$={\sum_{d=1}^{min(N,M)}}{\mu(d)}{\sum_{d|i}^{N}}1{\sum_{d|j}^{M}}1$
$={\sum_{d=1}^{min(N,M)}}{\mu(d)}{\sum_{i=1}^{\left \lfloor {\frac{N}{d}} \right \rfloor}}1{\sum_{j=1}^{\left\lfloor{\frac{M}{d}}\right\rfloor}}1$
$={\sum_{d=1}^{min(N,M)}}{\mu(d)}{\left\lfloor{\frac{N}{d}}\right\rfloor}{\left\lfloor{\frac{M}{d}}\right\rfloor}$
最后整除分块预处理$\mu$的前缀和搞一下就好了
代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 const int N=50010; 5 int prime[N]; 6 int miu[N]; 7 int s[N]; 8 bool vis[N]; 9 int cnt; 10 int T; 11 int a,b,d; 12 void gtp(void) 13 { 14 miu[1]=1; 15 for(int i=2;i<N;i++) 16 { 17 if(!vis[i]) 18 { 19 prime[++cnt]=i; 20 miu[i]=-1; 21 } 22 for(int j=1;j<=cnt&&prime[j]*i<N;j++) 23 { 24 vis[prime[j]*i]=true; 25 if(i%prime[j]==0) 26 { 27 miu[i*prime[j]]=0; 28 break; 29 } 30 miu[prime[j]*i]=-miu[i]; 31 } 32 } 33 for(int i=1;i<N;i++) 34 s[i]=s[i-1]+miu[i]; 35 return ; 36 } 37 int main() 38 { 39 gtp(); 40 scanf("%d",&T); 41 while(T--) 42 { 43 scanf("%d%d%d",&a,&b,&d); 44 a=a/d; 45 b=b/d; 46 int c=std::min(a,b); 47 int ans=0; 48 for(int k=1,u;k<=c;k=u+1) 49 { 50 u=std::min(a/(a/k),b/(b/k)); 51 ans+=(s[u]-s[k-1])*(a/k)*(b/k); 52 } 53 printf("%d\n",ans); 54 } 55 return 0; 56 }