[poj3904]Sky Code_状态压缩_容斥原理
Sky Code poj-3904
题目大意:给你n个数,问能选出多少满足题意的组数。
注释:如果一个组数满足题意当且仅当这个组中有且只有4个数,且这4个数的最大公约数是1,$1\le n\le 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\n",answer);//输出答案即可
}
return 0;
}
小结:如果一个问题极其复杂,我们不妨反其道而行之。容斥原理就是一例。
| 欢迎来原网站坐坐! >原文链接<

浙公网安备 33010602011771号