1. 程式人生 > >【HDU4869】Turn the pokers-思維

【HDU4869】Turn the pokers-思維

測試地址:Turn the pokers 題目大意:mm張牌,一開始正面都朝下,有nn次操作,每次操作給出一個XiX_i,表示要從這些牌中選出XiX_i張翻面,求所有操作完成後能得到多少種不同的正/反面序列。 做法: 本題需要用到思維。 因為將牌做任何置換都是合法的,因此只要我們能構造出最後有kk張正面的情況,就會對答案有CmkC_m^k的貢獻,因此問題就變成求哪些kk可以得到。 這裡有一個結論:如果kk的最小值是LL,最大值是RR,那麼在區間[L,R][L,R]中所有與LLRR關於22同餘的數都是合法的kk。這個東西如果一下子無法理解,可以使用數學歸納的思想。假設某一次操作前滿足這個性質,我們只要證明經過一次操作後還是滿足這個性質即可。具體的證明各種分類討論比較麻煩,但感性理解還是可以的。因此我們只要求L

LRR即可。 我們假設已經求出第ii次操作前的L,RL,R,那麼: 如果XiLX_i\le L,那麼newL=LxnewL=L-x; 否則如果XiRX_i\ge R,那麼newL=xRnewL=x-R; 否則,如果XiX_iLL關於22同餘,newL=0newL=0,否則newL=1newL=1。 這樣的分類討論應該還是不難理解的,就是能把正面翻過去就把正面翻過去,這樣正面的數量就最小。 而newRnewR的討論相似,只不過是能翻反面翻反面。這樣我們就解決了這一題。 以下是本人程式碼:

#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; }