GCD (HDU - 1695)莫比乌斯反演入门题

题目链接: https://vjudge.net/problem/HDU-1695
题意: 给你 a , b , c , d , k 五个值 (题目说明了 你可以认为 a=c=1) x 属于 [1,b] ,y属于[1,d] 让你求有多少对这样的 (x,y)满足gcd(x,y)==k。
且gcd(1,2)与gcd(2,1)是相同的,只算一种
思路: 首先我们可以转化一下问题 g c d ( x , y ) = = k g c d ( x / k , y / k ) = = 1 gcd(x,y)==k\Longrightarrow gcd(x/k,y/k)==1 此时,走到这一步,我们已经求得了(x,y)满足 gcd(x,y)=1 的对数 ,在反演过程我们用除法分块优化一下,将复杂度从 n n 降为 s q r t ( n ) sqrt(n)
但题目中说明了,(1,2)和(2,1)算一种情况,那么我们就要减去多余了的情况,去重

附上大佬的题解链接

https://blog.csdn.net/lixuepeng_001/article/details/50577932

#include<algorithm>
#include<iostream>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int N=1e5+5;
int mu[N],prime[N];
bool vis[N];
int cnt;
void get_mo()
{
	mu[1]=1;
	cnt=0;
	for(int i=2;i<N;i++)
	{
		if(!vis[i])
		{
			prime[cnt++]=i;
			mu[i]=-1;
		}
		for(int j=0;j<cnt&&i*prime[j]<N;j++)
		{
			vis[i*prime[j]]=1;
			if(i%prime[j])
				mu[i*prime[j]]=-mu[i];
			else
			{
				mu[i*prime[j]]=0;
				break;
			}
		}
	}
	for(int i=1;i<N;i++)
		mu[i]+=mu[i-1];
}
int main()
{
	//freopen("in.txt","r",stdin);
	get_mo();
	int n;
	scanf("%d",&n);
	for(int ii=1;ii<=n;ii++)
	{
		int a,b,c,d,k;
		scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
		if(k==0)
		{
			printf("Case %d: 0\n",ii);
			continue;
		}
		b/=k;
		d/=k;
		ll ans1=0,ans2=0,last;
		if(b>d)
			swap(b,d);
		for(int i=1;i<=b;i=last+1)
		{
			last=min(b/(b/i),d/(d/i));
			ans1+=1ll*(mu[last]-mu[i-1])*(b/i)*(d/i);
		}
		for(int i=1;i<=b;i=last+1)
		{
			last=b/(b/i);
			ans2+=1ll*(mu[last]-mu[i-1])*(b/i)*(b/i);
		}
		printf("Case %d: %lld\n",ii,ans1-ans2/2);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42819598/article/details/89266076