1. 程式人生 > >poj 3904 莫比烏斯反演 或 容斥原理

poj 3904 莫比烏斯反演 或 容斥原理

poj 3904 莫比烏斯反演 或 容斥原理
題目:
給出n個數字a1,a2,...an, 求從中選出一個四元組(a,b,c,d), 使得gcd(a,b,c,d)=1,求符合條件的四元組的數目。

限制:
1 <= n <= 1e4; 1 <= ai <= 1e4

思路:
莫比烏斯反演入門題
設f(k)為gcd(a,b,c,d)=k的四元組的數目,
設F(k)為gcd(a,b,c,d)為k的倍數的四元組的數目,
F(k)可以通過這個方式得到:
先通過對每個ai分解因數預處理處理出來,對於每個k,有多少個ai是它的倍數,假設為m,然後F(k)=C(m,4)。
令lim=max(a1,a2,...,an)
最後f(1)=mu[1]*F(1) + mu[2]*F(2) + ... + mu[lim]*F(lim)

/*poj 3904
  題目:
  給出n個數字a1,a2,...an, 求從中選出一個四元組(a,b,c,d), 使得gcd(a,b,c,d)=1,求符合條件的四元組的數目。
  限制:
  1 <= n <= 1e4; 1 <= ai <= 1e4
  思路:
  莫比烏斯反演入門題
  設f(k)為gcd(a,b,c,d)=k的四元組的數目,
  設F(k)為gcd(a,b,c,d)為k的倍數的四元組的數目,
  F(k)可以通過這個方式得到:
  先通過對每個ai分解因數預處理處理出來,對於每個k,有多少個ai是它的倍數,假設為m,然後F(k)=C(m,4)。
  令lim=max(a1,a2,...,an)
  最後f(1)=mu[1]*F(1) + mu[2]*F(2) + ... + mu[lim]*F(lim)
 */
#include
#include
#include
#include
using namespace std;
#define LL __int64
const int N=100005;
int cnt[N];
int mu[N];
void getMu(){
	for(int i=1;in-m;--i)
		ret*=(LL)i;
	for(int i=2;i<=m;++i)
		ret/=i;
	return ret;
}
LL gao(int x,int c){
	LL ret=C(c,4);
	return mu[x]*ret;
}
int main(){
	getMu();
	int n,a,_max;
	while(scanf("%d",&n)!=EOF){
		_max=0;
		memset(cnt,0,sizeof(cnt));
		for(int i=0;i=4)
				ans+=gao(i,cnt[i]);
		}
		printf("%I64d\n",ans);
	}
	return 0;
}


同樣用容斥也可以做

#include
#include
#include
#include
using namespace std;
#define LL __int64
const int N=100005;
int cnt[N];
LL C(int n,int m){
	if(nn-m;--i)
		ret*=(LL)i;
	for(int i=2;i<=m;++i)
		ret/=i;
	return ret;
}
LL gao(int x,int c){
	LL ret=C(c,4);
	int lim=sqrt(x);
	int flag=0;
	for(int i=2;i<=lim;++i){
		if(x%i==0){
			++flag;
			x/=i;
		}
		if(x%i==0) return 0;
	}
	if(x!=1) ++flag;
	if(flag%2==0) return ret;
	else return -ret;
}
int main(){
	int n,a,_max;
	while(scanf("%d",&n)!=EOF){
		_max=0;
		memset(cnt,0,sizeof(cnt));
		for(int i=0;i=4)
				ans+=gao(i,cnt[i]);
		}
		printf("%I64d\n",ans);
	}
	return 0;
}