POJ 3904-Sky Code 容斥原理

按照惯例,先附上题目链接http://poj.org/problem?id=3904

这个题大体意思为给你一组数,让你4个一组,看有多少种组合,满足它们的公因子为1.

先来说下这个题目有什么难点,一是组合数目的问题N≤1e5, 还好最多知道C(1e5, 4), long long int 应该够了二是查找哪些组合满足条件,假设我们遵奉"万物皆可暴力"大法,不停地枚举,直接C(n, 4),测评姬估计要TLE了。三是四个数的公因子为1,这个还好,看成是gcd的拓展版就可。

所以此题关键就在于找出哪些组合满足条件~~。

四个数的公约数为1,并不代表四个数两两互质。比如(2,3,4,5)公约数为1,但是2和4并不互质。从反面考虑,先求出四个数公约数不为1的情况个数,用总的方案个数减去四个数公约数不为1的情况个数就是所求。求四个数公约数不为1的情况个数,需要将N个数每个数质因数分解,纪录下所有不同的素因子所能组成的因子(就是4个数的公约数),并统计构成每种因子的素因子个数,和因子总数。然后再计算组合数。比如说因子2的个数为a,则四个数公约数为2的个数为C(a,4),因子3的个数为b,则四个数公约数为3的个数为C(b,4),因子6(2*3)的个数为c,则四个数公约数的个数为C(c,4)。但是公约数为2的情况中或者公约数为3的情况中可能包括公约数为6的情况,相当于几个集合求并集,这就需要容斥定理来做。容斥原理应用,以2为因子的数有a个,3为因子 的数有b个,6为因子的数有c个,n个数不互质的四元组个数为C(a,4)+C(b,4)-C(c,4) (含奇数个素因子的加,偶数个素因子的减下面就是统计出2,3,5这些因子的倍数的个数,对C(a,4)容斥!

AC代码:

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

typedef long long ll;
const int maxn = 10005;

ll prime[maxn], p[maxn], num[maxn], count[maxn];
void init()
{
	memset(p, 0, sizeof(p));
	memset(num, 0, sizeof(num));
	for(ll i = 4; i < maxn; i++){
		p[i] = i*(i-1)*(i-2)*(i-3)/24;
	}
}

void solve(int n)
{
	int cnt = 0;
	for(int i = 2; i*i <= n; i++){
		if(n % i == 0){
			prime[cnt++] = i;
			n /= i;
		}
		while(n % i == 0){
			n /= i;
		}
	}
	if(n != 1)	prime[cnt++] = n;
	for(int i = 1; i < (1 << cnt); i++){	//	以二进制形式来枚举 
		int k = 1, sum = 0;
		for(int j = 0; j < cnt; j++){
			if(i & (1<<j)){
				k *= prime[j];
				sum++;
			}
		}
		count[k]++;
		num[k] = sum;
	}
}

int main()
{
	init();
	int n, m;
	while(~scanf("%d", &n)){
		memset(count, 0, sizeof(count));
		for(int i = 0; i < n; i++){
			scanf("%d", &m);
			solve(m);
		}
		ll ans = 0;
		for(int i = 0; i < maxn; i++){
			if(count[i]){
				if(num[i]&1)
					ans += p[count[i]];
				else	ans -= p[count[i]];
			}
		}
		cout << p[n] - ans << endl;
	}
	return 0;
}
容斥原理关键在思维啊


猜你喜欢

转载自blog.csdn.net/water_zero_saber/article/details/79964496
今日推荐