[bzoj2339][HNOI2011]卡農——動態規劃+容斥原理
阿新 • • 發佈:2018-12-12
題目大意:
眾所周知卡農是一種復調音樂的寫作技法,小余在聽卡農音樂時靈感大發,發明了一種新的音樂譜寫規則。他將聲音分成 n 個音階,並將音樂分成若干個片段。音樂的每個片段都是由 1 到 n 個音階構成的和聲,即從 n 個音階中挑選若干個音階同時演奏出來。為了強調與卡農的不同,他規定任意兩個片段所包含的音階集合都不同。同時為了保持音樂的規律性,他還規定在一段音樂中每個音階被奏響的次數為偶數。現在的問題是:小余想知道包含 m 個片段的音樂一共有多少種。兩段音樂 a 和 b 同種當且僅當將 a 的片段重新排列後可以得到 b。例如:假設 a
為{{1,2},{2,3}},b 為{{3,2},{2,1}},那麼 a 與 b 就是同種音樂。由於種數很多,你只需要
輸出答案模 100000007(質數)的結果。
思路:
首先我們可以得到一共有\(2^n-1\)種合法的集合。
由於每一個元素都必須要出現偶數次,所以我們如果選了\(m-1\)個片段,那麼最後一個片段就可以直接根據奇偶性確定。
從\(2^n-1\)個片段中選擇\(m-1\)個的方案為\({2^n-1\choose m-1}\),這時候組合數顯然難以計算,於是我們將片段之間從無序變得有序,之後再將答案除以一個\(m!\)即可。
考慮記\(f_m\)為m個片段合法的方案數,如果令\(f_m=A_{2^n-1}^{m-1}\),那麼我們會發現最後一個片段有可能為空集,同時也有可能是之前出現過的片段,於是我們要將這些不合法的情況給刪除。
如果是空集,那麼可以保證前\(m-1\)
如果是前面出現過的片段,考慮列舉和它相同的片段的位置是哪一個(總共\(m-1\)個位置),然後剩下來的\(m-2\)個片段的情況也一定是合法的,共有\(f_{m-2}\)種,確定了這\(m-2\)中之後,有\(2^n-1-(m-2)\)個片段可以供這個位置挑選,所以要減去的情況是\((m-1)\times f_{m-2}\times (2^n-1-(m-2))\)。
然後就可以直接遞推了。
#include<bits/stdc++.h> #define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i) #define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i) #define MREP(i,x) for(int i=beg[x],v;v=to[i],i;i=las[i]) #define debug(x) cout<<#x<<"="<<x<<endl #define fi first #define se second #define mk make_pair #define pb push_back typedef long long ll; using namespace std; void File(){ freopen("bzoj2339.in","r",stdin); freopen("bzoj2339.out","w",stdout); } template<typename T>void read(T &_){ T __=0,mul=1; char ch=getchar(); while(!isdigit(ch)){ if(ch=='-')mul=-1; ch=getchar(); } while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar(); _=__*mul; } const int maxn=1e6+10; const ll mod=1e8+7; int n,m; ll p2,fac[maxn],f[maxn]; ll qpow(ll x,ll y){ ll ret=1; x%=mod; while(y){ if(y&1)ret=ret*x%mod; x=x*x%mod; y>>=1; } return ret; } int main(){ //File(); read(n),read(m); p2=(qpow(2,n)-1)%mod; fac[0]=1; REP(i,1,m)fac[i]=fac[i-1]*(p2-i+1)%mod; f[0]=1,f[1]=0; REP(i,2,m){ f[i]=fac[i-1]; f[i]=(f[i]-f[i-1])%mod; f[i]=(f[i]-(i-1)*f[i-2]%mod*(p2-i+2)%mod)%mod; } ll tmp=1; REP(i,1,m)tmp=tmp*i%mod; f[m]=f[m]*qpow(tmp,mod-2)%mod; printf("%lld\n",(f[m]+mod)%mod); return 0; }