1. 程式人生 > >【BZOJ2339】【HNOI2011】卡農

【BZOJ2339】【HNOI2011】卡農

void 題解 noi log scan online const 情況 二進制表示

技術分享

題解:

  首先用二進制表示每個音階是否使用,那麽共有$2^{n}-1$(空集不可行)種片段,用$a_{i}$來表示每個片段,問題就是求滿足$a_{1}\left (xor\right)a_{2}\left (xor\right)......\left (xor\right)a_{m}==0\&\&a_{i}!=a_{j},1<=i<j<=m$的方案數,我們用$f_{i}$表示片段數為i時,且滿足前面式子的答案。

  那麽首先我們在選取i個片段時,必然是由前i-1個片段決定的,所以共有$A_{2^{n}-1}^{i-1}$種選取方案。其中若i-1個時已滿足其異或和為0,那麽此時是不合法的,所以需要減去$f_{i-1}$,考慮出現重復的情況,因為出現了重復,又有異或的逆運算就是本身,這也就意味著除去兩個重復的片段的i-2個片段已經滿足其異或和為0,而這個重復的片段在i-1個片段中的位置有i-1種,而這個重復的片段的值又可以在除去i-2個片段的集合中任意選取。

  所以得到遞推式:

  $$f_{i}=A_{2^{n}-1}^{i-1}-f_{i-1}-f_{i-2}*(2^{n}-1-i+2)*(i-1)$$

  又由於不允許有重復,在最後除去$m!$即可。

 

 1 #include<cstdio>
 2 typedef long long ll;
 3 const ll mod=100000007;
 4 const int N=1000100;
 5 ll n,m;
 6 ll powmod(ll a,ll b){
 7     ll ans=1;
 8     a%=mod;
 9     for(;b;b>>=1
,a=a*a%mod) 10 if(b&1) ans=ans*a%mod; 11 return ans; 12 } 13 ll tot; 14 ll jie; 15 ll fac[N]; 16 inline void init(){ 17 fac[0]=1; 18 for(ll i=1;i<=m;i++) 19 fac[i]=fac[i-1]*(tot-i+1)%mod; 20 } 21 22 ll f[N]; 23 int main(){ 24 scanf("%lld%lld",&n,&m);
25 tot=powmod(2LL,n); 26 tot--; 27 if(tot<0) tot+=mod; 28 init(); 29 for(ll i=3;i<=m;i++){ 30 f[i]=(fac[i-1]-f[i-1])%mod-f[i-2]*(i-1)%mod*(tot-i+2)%mod; 31 f[i]%=mod; 32 } 33 ll tt=1; 34 for(ll i=1;i<=m;++i) 35 tt=tt*i%mod; 36 tt=powmod(tt,mod-2); 37 printf("%lld\n",(f[m]*tt%mod+mod)%mod); 38 }

【BZOJ2339】【HNOI2011】卡農