BZOJ2301: [HAOI2011]Problem b
【传送门:BZOJ2301】
简要题意:
给出a,b,c,d,k,求出满足a<=x<=b,c<=y<=d的数对中gcd(x,y)==k的数对数
题解:
莫比乌斯反演模板题
设F(t)为gcd(x,y)%t==0的数对数,f(t)为gcd(x,y)==t的数对数
然后可以得到$F(n)=\sum_{n|d}f(d)$,根据公式得到$f(n)=\sum_{n|d}\mu(\frac{d}{n})F(d)$
因为F(t)=b/t*d/t,所以可以得到$f(1)=\sum_{i=1}^{min(b/k,d/k)}\mu(i)*\frac{b}{k}*\frac{d}{k}$
然后整除分块加速,容斥一下就可以了
参考代码:
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> using namespace std; typedef long long LL; int miu[51000],prime[51000],v[51000]; int sum[51000]; void pre(int n) { memset(v,0,sizeof(v)); memset(miu,0,sizeof(miu));miu[1]=1; int m=0; for(int i=2;i<=n;i++) { if(v[i]==0) { v[i]=i; prime[++m]=i; miu[i]=-1; } for(int j=1;j<=m;j++) { if(prime[j]>v[i]||prime[j]>n/i) break; v[i*prime[j]]=prime[j]; if(i%prime[j]==0){miu[i*prime[j]]=0;break;} else miu[i*prime[j]]=-miu[i]; } } sum[0]=0; for(int i=1;i<=n;i++) sum[i]=sum[i-1]+miu[i]; } LL getd(int x,int y) { int last;LL ans=0; for(int i=1;i<=min(x,y);i=last+1) { last=min(x/(x/i),y/(y/i)); ans+=(LL)(sum[last]-sum[i-1])*(x/i)*(y/i); } return ans; } int main() { pre(50000); int T; scanf("%d",&T); while(T--) { int a,b,c,d,k; scanf("%d%d%d%d%d",&a,&b,&c,&d,&k); LL ans1=0,ans2=0; printf("%lld\n",getd(b/k,d/k)-getd((a-1)/k,d/k)-getd(b/k,(c-1)/k)+getd((a-1)/k,(c-1)/k)); } return 0; }
渺渺时空,茫茫人海,与君相遇,幸甚幸甚