測試地址:Turn the pokers
題目大意: 有
張牌,一開始正面都朝下,有
次操做,每次操做給出一個
,表示要從這些牌中選出
張翻面,求全部操做完成後能獲得多少種不一樣的正/反面序列。
作法: 本題須要用到思惟。
由於將牌作任何置換都是合法的,所以只要咱們能構造出最後有
張正面的狀況,就會對答案有
的貢獻,所以問題就變成求哪些
能夠獲得。
這裏有一個結論:若是
的最小值是
,最大值是
,那麼在區間
中全部與
和
關於
同餘的數都是合法的
。這個東西若是一會兒沒法理解,可使用數學概括的思想。假設某一次操做前知足這個性質,咱們只要證實通過一次操做後仍是知足這個性質便可。具體的證實各類分類討論比較麻煩,但感性理解仍是能夠的。所以咱們只要求
和
便可。
咱們假設已經求出第
次操做前的
,那麼:
若是
,那麼
;
不然若是
,那麼
;
不然,若是
和
關於
同餘,
,不然
。
這樣的分類討論應該仍是不難理解的,就是能把正面翻過去就把正面翻過去,這樣正面的數量就最小。
而
的討論類似,只不過是能翻反面翻反面。這樣咱們就解決了這一題。
如下是本人代碼:php
#include <bits/stdc++.h> using namespace std; typedef long long ll; const ll mod=1000000009; int n,m; ll fac[100010],inv[100010],invfac[100010]; ll C(ll n,ll m) { return fac[n]*invfac[m]%mod*invfac[n-m]%mod; } int main() { while(scanf("%d%d",&n,&m)!=EOF) { fac[0]=fac[1]=inv[1]=invfac[0]=invfac[1]=1; for(ll i=2;i<=m;i++) { fac[i]=fac[i-1]*i%mod; inv[i]=(mod-mod/i)*inv[mod%i]%mod; invfac[i]=invfac[i-1]*inv[i]%mod; } int L=0,R=0; for(int i=1;i<=n;i++) { int x; scanf("%d",&x); int nxtL,nxtR; if (x<=L) nxtL=L-x; else if (x>=R) nxtL=x-R; else nxtL=(x-L)%2; if (x<=m-R) nxtR=R+x; else if (x>=m-L) nxtR=2*m-L-x; else nxtR=m-((x-m+R)%2); L=nxtL,R=nxtR; } ll ans=0; for(int i=L;i<=R;i+=2) ans=(ans+C(m,i))%mod; printf("%lld\n",ans); } return 0; }
#include <bits/stdc++.h> using namespace std; typedef long long ll; const ll mod=1000000009; int n,m; ll fac[100010],inv[100010],invfac[100010]; ll C(ll n,ll m) { return fac[n]*invfac[m]%mod*invfac[n-m]%mod; } int main() { while(scanf("%d%d",&n,&m)!=EOF) { fac[0]=fac[1]=inv[1]=invfac[0]=invfac[1]=1; for(ll i=2;i<=m;i++) { fac[i]=fac[i-1]*i%mod; inv[i]=(mod-mod/i)*inv[mod%i]%mod; invfac[i]=invfac[i-1]*inv[i]%mod; } int L=0,R=0; for(int i=1;i<=n;i++) { int x; scanf("%d",&x); int nxtL,nxtR; if (x<=L) nxtL=L-x; else if (x>=R) nxtL=x-R; else nxtL=(x-L)%2; if (x<=m-R) nxtR=R+x; else if (x>=m-L) nxtR=2*m-L-x; else nxtR=m-((x-m+R)%2); L=nxtL,R=nxtR; } ll ans=0; for(int i=L;i<=R;i+=2) ans=(ans+C(m,i))%mod; printf("%lld\n",ans); } return 0; }