Luogu3214 [HNOI2011]卡農
阿新 • • 發佈:2019-03-26
-i pac splay enable n) sin hnoi2011 lin code
Luogu3214 [HNOI2011]卡農
題面:Luogu
解析
題意即為選出\(m\)個不同的數,使其異或和為0,發現其實就是先選前\(m-1\)個數,然後最後一個數應該等於前面所有數的異或和,這樣計算兩種不合法的情況:1.最後一個數為0。2.最後一個數與前面的數重復。先看1,發現是選\(m-1\)個數異或和為0,再看2,考慮刪去重復的2個數,重復的數的位置有\(m-1\)中,取值有\(S-(i-2)\)中(因為不能與其他的數重復),那麽剩下的\(m-2\)個數異或和應為0。那麽設\(f(i)\)表示選\(i\)個數異或和為0的方案數,有:
\[f(i)=C[S][i-1]-f[i-1]-(S-i+2)(i-1)f[i-2]\]
最後註意需要消除順序的影響。
代碼
// luogu-judger-enable-o2 #include<cstdio> #define N 1000005 using namespace std; const int P=100000007; inline int In(){ char c=getchar(); int x=0,ft=1; for(;c<'0'||c>'9';c=getchar()) if(c=='-') ft=-1; for(;c>='0'&&c<='9';c=getchar()) x=x*10+c-'0'; return x*ft; } int n,m,pow_2,_inv,A[N],f[N]; inline int power(int x,int k){ int s=1,t=x; for(;k;k>>=1,t=1ll*t*t%P) if(k&1) s=1ll*s*t%P; return s; } int main(){ n=In(); m=In(); pow_2=(power(2,n)-1+P)%P; _inv=1; for(int i=1;i<=m;++i) _inv=1ll*i*_inv%P; _inv=power(_inv,P-2); A[1]=pow_2; for(int i=1;i<m;++i) A[i+1]=1ll*(pow_2-i+P)%P*A[i]%P; f[0]=1; for(int i=2;i<=m;++i) f[i]=((A[i-1]-f[i-1]+P)%P-1ll*(pow_2-i+2+P)%P*(i-1)%P*f[i-2]%P+P)%P; printf("%lld\n",1ll*f[m]*_inv%P); return 0; }
Luogu3214 [HNOI2011]卡農