[POI2007]ZAP-Queries,洛谷P3455,莫比乌斯反演

正题

      这题出得很明显,直接用莫比乌斯反演求gcd(x,y)=d.

      你可以设一个函数f(d)表示1~a和1~b中gcd为d的对数。

      那么要学会一个套路就是设一个求和函数F(d)表示1~a和1~b中  gcd为d的倍数  的对数。

      很明显F(d)=f(d)+f(2d)+f(3d)...

      F(d)当然也可以通过一条式子就算出来=(a/d)*(b/d).

      接下来的就是看怎么除法分块优化了咯,具体看代码因为我不知道怎么用数学符号表达。。emm

代码<没有分很多函数不要介意>

#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;

int n;
int d,a,b;
int p[50010];
int miu[50010];
bool tf[50010];
int sum[50010];

long long solve(){
	long long ans=0;
	int l=1,r;
	a/=d;b/=d;//先除一个d,问题就转化为求1到a和1到b中有多少对互质的对数
	while(l<=min(a,b)){//l枚举的是当前累加到f(lx)
		r=min(a/(a/l),b/(b/l));//除法分块算出右端点,这时候可以知道l~r之间的F(ix)(l<=i<=r)取值都是一样的.
		ans=ans+(long long)(a/l)*(b/l)*(sum[r]-sum[l-1]);//所以乘一下这段的miu值和,可以用前缀和算
		l=r+1;
	}
	return ans;
}

int main(){
	scanf("%d",&n);
	miu[1]=1;
	sum[1]=1;
	for(int i=2;i<=50000;i++){//线性筛莫比乌斯函数
		if(!tf[i]) {miu[i]=-1;p[++p[0]]=i;}
		for(int j=1;j<=p[0] && (long long)i*p[j]<=50000;j++){
			tf[p[j]*i]=true;
			if(i%p[j]==0) {miu[i*p[j]]=0;break;}
			miu[i*p[j]]=-miu[i];
		}
		sum[i]=sum[i-1]+miu[i];//前缀和算出来以便于后面的求解
	}
	while(n--){
		scanf("%d %d %d",&a,&b,&d);
		printf("%lld\n",solve());//进入函数求解
	}
}

猜你喜欢

转载自blog.csdn.net/Deep_Kevin/article/details/80229464