1. 程式人生 > >[poj3904]Sky Code_狀態壓縮_容斥原理

[poj3904]Sky Code_狀態壓縮_容斥原理

LG sep cst ostream 發現 middle 註釋 print sizeof

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;
}

    小結:如果一個問題極其復雜,我們不妨反其道而行之。容斥原理就是一例。

[poj3904]Sky Code_狀態壓縮_容斥原理