测试地址: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; }