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;
}


posted @ 2015-10-12 17:04  orzpps  阅读(125)  评论(0编辑  收藏  举报