Sky Code poj-3904
题目大意:给你n个数,问能选出多少满足题意的组数。
注释:如果一个组数满足题意当且仅当这个组中有且只有4个数,且这4个数的最大公约数是1,$1le nle 10^4$。
想法:我们显然可以知道4个数是可以不用两两互质的,所以正面计算难度较大,我们考虑从反面考虑。我们通过计算所有gcd不为1的组数,用总组数相减即可。然后,我们发现一个不为0的gcd显然可以被组中的任意一个数整除,所以我们可以进行容斥。只需要枚举gcd的约数个即可。计算的过程我们用状态压缩实现。
最后,附上丑陋的代码... ...
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define maxn 10005 using namespace std; typedef long long ll; ll a[10010];//记录单个数的质因数 ll cnt;//记录单个数的质因数个数 ll ans[10010][2];//ans[i][0]表示包含i这个因子的数的个数,ans[i][1]表示i的质因子个数 ll Calc(ll x)//计算C[n][4] { return x*(x-1)*(x-2)*(x-3)/24; } void separate(ll x)//分解质因数,由于我们在后面需要用cnt进行状态压缩,所以a数组从0开始记录 { for(int i=2;i*i<=x;i++) { if(x%i==0) { a[cnt]=i; cnt++; while(x%i==0) { x/=i; } } } if(x>1) a[cnt++]=x; } void dispose(ll x) { cnt=0; separate(x); for(int i=1;i<(1<<cnt);i++)//通过枚举当前全集来统计桶 { ll flag=0,middle=1; for(int j=0;j<cnt;j++) { if(i&(1<<j)) { flag++; middle*=a[j]; } } ans[middle][0]++; ans[middle][1]=flag; } } int main() { ll n; while(~scanf("%lld",&n)) { memset(ans,0,sizeof ans); ll x; for(int i=1;i<=n;i++) { scanf("%lld",&x); dispose(x); } ll answer=Calc(n); for(int i=2;i<=maxn/4;i++) { if(ans[i][0])//Important { if(1&ans[i][1]) answer-=Calc(ans[i][0]);//如果是偶数个质因子 else answer+=Calc(ans[i][0]);//如果是奇数个质因子 } } // puts("Fuck"); printf("%lld ",answer);//输出答案即可 } return 0; }
小结:如果一个问题极其复杂,我们不妨反其道而行之。容斥原理就是一例。