[POI2007]ZAP-Queries

洛咕

题意:给定\(a,b,d\),求\(\sum_{i=1}^{a}\sum_{j=1}^{b}(gcd(i,j)==d)\)

分析:莫比乌斯反演在\(gcd\)问题上的运用都有一定的套路.设,

\(f(d)=\sum_{i=1}^{a}\sum_{j=1}^{b}(gcd(i,j)==d)\)

\(F(d)=\sum_{d|x}^{n}f(x)\)

仔细研究一下\(F(d)\)的式子,不难发现\(F(d)\)就是指在1到\(a\)和1到\(b\)中各选1个数,它们的\(gcd\)\(d\)的倍数的方案数,那么显然有式子\(F(d)=\lfloor\frac ad\rfloor\lfloor\frac bd\rfloor\)

又由莫比乌斯反演定理可得,\(f(d)=\sum_{d|x}μ(\frac xd)F(x)\)

代入\(F(x)=\lfloor\frac ax\rfloor\lfloor\frac bx\rfloor\)得,\(f(d)=\sum_{d|x}μ(\frac xd)\lfloor\frac ax\rfloor\lfloor\frac bx\rfloor\)

\(ans=f(d)=\sum_{d|x}μ(\frac xd)\lfloor\frac ax\rfloor\lfloor\frac bx\rfloor=\sum_{x=1}^{min(a/d,b/d)}μ(\frac xd)\lfloor\frac ax\rfloor\lfloor\frac bx\rfloor\)

预处理出\(μ\)函数的前缀和,然后整除分块就好了.

#include<bits/stdc++.h>
using namespace std;
inline int read(){
   int s=0,w=1;char ch=getchar();
   while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
   while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
   return s*w;
}
const int N=50005;
int mu[N],prime[N],v[N],sum[N];
inline void get_mu(){
    int m=0;mu[1]=1;
    for(int i=2;i<=N;i++){
		if(!v[i]){
	    	prime[++m]=i;
	    	v[i]=i;
	    	mu[i]=-1;
		}
		for(int j=1;j<=m;j++){
	    	if(i*prime[j]>N)break;
	    	v[i*prime[j]]=prime[j];
	    	if(i%prime[j]==0)break;
	    	else mu[i*prime[j]]=-mu[i];
		}
    }
    for(int i=1;i<=N;i++)sum[i]=sum[i-1]+mu[i];
}
int main(){
    get_mu();
    int n=read();
    while(n--){
		int a=read(),b=read(),d=read();
		a/=d;b/=d;int ans=0;
		for(int l=1,r;l<=min(a,b);l=r+1){
	    	r=min(a/(a/l),b/(b/l));
	    	ans+=(a/l)*(b/l)*(sum[r]-sum[l-1]);
		}
		printf("%d\n",ans);
    }
    return 0;
}

posted on 2019-05-11 14:01  PPXppx  阅读(84)  评论(0编辑  收藏  举报