[poj3904]Sky Code_狀態壓縮_容斥原理
阿新 • • 發佈:2018-03-19
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_狀態壓縮_容斥原理