【BZOJ2339】【HNOI2011】卡農
阿新 • • 發佈:2017-08-26
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】卡農