hdu1695(莫比乌斯反演+容斥)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1695

题目是求 在区间[a,b]选一个数x,区间[c,d]选一个数y,求满足gcd(x,y) = k 的个数

题目给出了条件,可以认为所有的样例中,a = b = 1,那么就是在区间[1,b]和区间[1,d]中分别选择两个数求gcd(x,y) = k的个数

我们让区间[1,b]和[1,d]变为[1,b/k]和[1,d/k],这里就可以转化为求gcd(x,y) = 1的个数

我们假设  f 是gcd(x,y) = n的个数,F 是gcd(x,y) = n的倍数的个数

显然 F 和 f  存在如下倍数关系,即:

                                                             

设b/k = m,d/k = n,若要求gcd(x,y) =  z 的倍数的个数,那么则F(z) 显然为  (m/z)*(n/z)

由莫比乌斯反演得:

                                

 

 

莫比乌斯函数:

 

其中莫比乌斯函数的线性筛模板如下:

ll prime[maxn],mu[maxn],vis[maxn],F[maxn];
void get_mu() {  //莫比乌斯函数线性筛 
	int N = maxn;
	memset(prime,0,sizeof(prime));
	memset(mu,0,sizeof(mu));
	memset(vis,0,sizeof(vis));
	mu[1] = 1;
	int 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;
			}
		}
	}
}

题目转化为在区间[1,b/k]和[1,d/k]中求gcd(x,y) = 1的对数,其实就是求f(1)了,直接计算f (1)。

因为题目要求如(2,1)和(1,2)是相同的实数对,所以要去重,而重复部分是在f(1)求解过程中的u(1)*F(1) + u(2)*F(2) + u(3)*F(3) + ........+u(min(m,n))*F(min(m,n)),这部分答案除以2是重复累加的,最后再计算一遍减去即可。

 

AC代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#define maxn 100005
using namespace std;
typedef long long ll;
const int INF = 1 << 30;
const int MOD = 1e9 + 7;
ll prime[maxn],mu[maxn],vis[maxn],F[maxn];
void get_mu() {  //莫比乌斯函数线性筛 
	int N = maxn;
	memset(prime,0,sizeof(prime));
	memset(mu,0,sizeof(mu));
	memset(vis,0,sizeof(vis));
	mu[1] = 1;
	int 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;
			}
		}
	}
}
int main(){
	int t;
	ios::sync_with_stdio(false);
	cin>>t;
	int cnt = 1;
	get_mu();
	while(t--){
	cout<<"Case "<<cnt<<": ";
	cnt++;
	int a,b,c,d,k;
	cin>>a>>b>>c>>d>>k;
	if (k == 0) {
		cout<<0<<endl;
		continue;
	}
	b/=k,d/=k;
	ll ans = 0,t = 0;
	for(int i= 1;i<=min(b,d);i++){
		ans+=(long long)mu[i]*(b/i)*(d/i);
	}
	for(int i = 1;i<=min(b,d);i++){
		t+=(long long)mu[i]*(min(b,d)/i)*(min(b,d)/i);//计算重复部分 
	}
	cout<<ans-t/2<<endl;
	}
}

 

 

 

 

posted @ 2019-10-20 12:06  AaronChang  阅读(99)  评论(0编辑  收藏  举报