POJ - 390 Sky Code

版权声明:博客作者为blue bear,严禁他人在未经作者同意下进行商用转载 https://blog.csdn.net/weixin_44354699/article/details/88372501

二进制 容斥原理


#题面
Stancu likes space travels but he is a poor software developer and will never be able to buy his own spacecraft. That is why he is preparing to steal the spacecraft of Petru. There is only one problem – Petru has locked the spacecraft with a sophisticated cryptosystem based on the ID numbers of the stars from the Milky Way Galaxy. For breaking the system Stancu has to check each subset of four stars such that the only common divisor of their numbers is 1. Nasty, isn’t it? Fortunately, Stancu has succeeded to limit the number of the interesting stars to N but, any way, the possible subsets of four stars can be too many. Help him to find their number and to decide if there is a chance to break the system.
Input
In the input file several test cases are given. For each test case on the first line the number N of interesting stars is given (1 ≤ N ≤ 10000). The second line of the test case contains the list of ID numbers of the interesting stars, separated by spaces. Each ID is a positive integer which is no greater than 10000. The input data terminate with the end of file.
Output
For each test case the program should print one line with the number of subsets with the asked property.

输入 输出
4
2 3 4 5
4
2 4 6 8
7
2 3 4 5 7 6 8
1

0

34

题意

从n个数中选择4个数使他们的GCD = 1,求总共有多少种方法。
对于每个数都进行质因数拆解。
比如样例三。

2=2
3=3
4=2*2
5=5
6=2*3
7=7
8=2*2*2

那么[2]=4,[3]=2,[5]=1,[7]=1;
以及[6]=1
个数小于4的不看
2的就是C(4,4)即质因数为2的有4个,4个里取4个。
再用C(n,4)即C(7,4)减去刚才的;
这样例质因数没用到容斥…
于是再讲一下二进制容斥
韦恩图
如果我们要求A∪B的话很显然就是A+B-(A∩B)对吧
在这里插入图片描述
接下来扩展到3个,AUBUC=A+B+C-A∩B-B∩C-A∩C+A∩B∩C
当N个的时候呢,奇加偶减。A,B,C是1是奇数相加,AB,BC,AC是2是偶数相减,ABC是3是奇数相加,至于原因,很明显我是不太懂的,但是这个规律是正确的。
回到我们的题,质因数个数为奇数个相加,偶数个相减就能去除重复计算的个数。x
什么又是二进制容斥原理呢。
我们用一个8位数来表示当前位是否被选中,比如00000001就是1被选中,00100001就是1和6被选中,其余同理。
但是我们怎么判断这个位上是否有1呢,这就需要使用我们的位运算按位与&了,如果一个数&1是1就是1被选中,反之则无。&(11)2同理。
说到此处你恍然大悟,注意上面的(11)2是二进制下的11也就是3。
你可能会有疑问,如果质因数很多是否2n会爆掉long long,当初我也有这个疑问,事实上,这个数需要很大才可以,至于是多少你可以让质数相乘看看。

代码

#include<stdio.h>
#include<string.h>
#include<math.h>
const int maxn=1e4+5;
typedef long long ll;
ll prime_son[maxn];//记录当前数字的质因子,[0]存放个数 
ll prime_num;
int cnt[maxn];//记录奇偶性用于容斥原理 
int num[maxn];//记录有该种因子的个数 
void init(ll n){//拆分质因子
	prime_num=0;
	ll lim=sqrt(n*1.0);
	for(ll i=2;i<=lim;i++){
		if(n%i==0) prime_son[prime_num++]=i;
		while(n%i==0) n/=i;
	} 
	if(n>1) prime_son[prime_num++]=n;
}
ll C(ll n,ll m){//组合数 n个取m个 
	if(n<m) return 0;
	ll ans=1;
	for(int i=1; i<=m;i++){
		ans*=n--;
		ans/=i;
	}
	return ans;
}

int main(){
	ll n,x;
	while(~scanf("%lld",&n)){
		memset(num,0,sizeof(num));
		for(int i=0;i<n;i++){
			scanf("%lld",&x);
			init(x);
			for(int j=1;j<(1ll<<prime_num);j++){
				int bits=0,temp=1;
				for(int k=0;k<prime_num;k++){
					if(j&(1ll<<k) ){
						bits++;
						temp*=prime_son[k];
					}
				}
				num[temp]++;//个数 
				cnt[temp]=bits;//奇偶性 
			}
		}
		ll ans=0;
		for(int i=2;i<=maxn;i++){
			//printf("num[%d]:%d\n",i,num[i]);
			if(num[i]<4) continue;
			if(cnt[i]&1) ans+=C(num[i],4);
			else ans-=C(num[i],4);
		}
		printf("%lld\n",C(n,4)-ans);
	} 
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44354699/article/details/88372501
sky