P3214 [HNOI2011]卡農
阿新 • • 發佈:2019-01-11
題目
在被一題容斥\(dp\)完虐之後,打算做一做集合容斥這類的題了
第一次深感HNOI的毒瘤(題做得太少了!!)
做法
求\([1,n]\)組成的集合中選\(m\)個不同集合且每個元素出現偶數的組合方案
無序(打亂順序仍記為一種)通常我們對於有序的做法更簡單,怎麼轉換呢
C組合數的公式是怎麼得來的?別說你是背來的\(emmm\)(那也沒有做這題的必要了)
有序\(m!\)就得到了無序的
我們考慮\(dp\),陣列\(dp_i\)表示選i個不同集合的排列方案
異或和為\(0\),則,確定前\(i-1\)個集合則第\(i\)個集合自然也出來了,方案數為\(A_{2^n-1}^{i-1}\)
如果前面\(i-1\)個集合異或和已為\(0\),那第\(i\)個集合為空集,不符題意,這部分的方案數就是\(dp_{i-1}\)
保證所選集合不重複,若\(i\)與前\(i-1\)任意重複,去掉這個重複的集合,為\(dp_{i-2}\),可能的位置有\((i-1)\)個,重複集合個數有\((2^n-1-(i-2))\)
\(dp_i=A_{2^n-1}^{i-1}-dp_{i-1}-dp_{i-2}*(i-1)*(2^n-i+1)\)
最後再乘下逆元就好了
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; typedef long long LL; const LL p=100000007; const int maxn=1e6+9; inline LL Pow(LL base,LL b){ LL ret(1); while(b){ if(b&1) ret=ret*base%p; base=base*base%p, b>>=1; } return ret; } LL n,m,a,Up,A,ans; LL dp[maxn]; int main(){ scanf("%lld%lld",&n,&m); dp[1]=dp[2]=0, Up=(Pow(2ll,n)-1ll+p)%p, A=Up; for(LL i=3;i<=m;++i) A=A*(Up-i+2)%p, dp[i]=((A-dp[i-1]+p)%p-dp[i-2]*(i-1)%p*((Up-(i-2)+p)%p)%p +p)%p; a=1; for(LL i=2;i<=m;++i) a=a*i%p; ans=dp[m]*Pow(a,p-2)%p; printf("%lld\n",ans); return 0; }/* 100 1000 */