bzoj2045: 双亲数&bzoj1101: [POI2007]Zap
传送门:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1101
思路:设a<b
求的就是f(a,b,d)=ΣΣ[gcd(i,j)==d](1<=i<=a,1<=j<=b)
又有公式Σmiu(d) =[n==1] (d|n) //miu是莫比乌斯函数
把n用gcd(i,j)代入得
ΣΣΣmiu(d)(d|gcd(i,j),1<=i<=a,1<=j<=b)
=Σmiu(d)*(a/d)*(b/d)分段统计即可
分成a/d和b/d都相同的2*sqrt(n)段,乘上这一段的miu的和即可O(sqrt(n))回答了
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=50010; int mu[maxn],A,B,d,pri[maxn],tot,ans,cas,smu[maxn];bool isp[maxn]; void prework(){ memset(isp,1,sizeof(isp)); isp[1]=0,mu[1]=1; for (int i=2;i<=maxn-10;i++){ if (isp[i]) mu[i]=-1,pri[++tot]=i; for (int j=1;j<=tot&&i*pri[j]<=maxn-10;j++){ isp[i*pri[j]]=0; if (!(i%pri[j])){mu[i*pri[j]]=0;break;} mu[i*pri[j]]=mu[i]*-1; } } for (int i=1;i<=maxn-10;i++) smu[i]=smu[i-1]+mu[i]; //for (ll i=1;i<=50;i++) printf("%lld %d\n",i,isp[i]); } int getans(int a,int b){ int ans=0,ed=0; for (int st=1;st<=B;st=ed+1){ ed=min(a/(a/st),b/(b/st)); ans+=(smu[ed]-smu[st-1])*(a/st)*(b/st); } return ans; } int main(){ prework(); scanf("%d",&cas); while (cas--){ ans=0; scanf("%d%d%d",&A,&B,&d); if (A<B) swap(A,B);A/=d,B/=d; printf("%d\n",getans(A,B)); } return 0; }