[hdu 4869](14年多校I題)Turn the pokers 找規律+拓歐逆元,hdupokers
Turn the pokers
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 316 Accepted Submission(s): 101
Problem Description
During summer vacation,Alice stay at home for a long time, with nothing to do. She went out and bought m pokers, tending to play poker. But she hated the traditional gameplay. She wants to change. She puts these pokers face down, she decided to flip poker n times, and each time she can flip Xi pokers. She wanted to know how many the results does she get. Can you help her solve this problem?
Input
The input consists of multiple test cases.
Each test case begins with a line containing two non-negative integers n and m(0<n,m<=100000).
The next line contains n integers Xi(0<=Xi<=m).
Output
Output the required answer modulo 1000000009 for each test case, one per line.
Sample Input
3 4
3 2 3
3 3
3 2 3
Sample Output
8
3
Hint
For the second example:
0 express face down,1 express face up
Initial state 000
The first result:000->111->001->110
The second result:000->111->100->011
The third result:000->111->010->101
So, there are three kinds of results(110,011,101)
題目大意
給定M張牌,可以翻轉N次,每次可以翻轉恰好Xi張牌,剛開始牌面全部朝下,問經過N次翻轉之後可能產生的撲克序列數(如範例hint)。
解題思路
現場還是沒出……想到dp的思路但複雜度高達N^2.
可以觀察到,我們最後正面朝上的牌的數量奇偶總是一定的(如1,3,5),因為不同奇偶情況就需要至少多翻一次,但翻動的次數已經固定不能更改。
考慮一次翻轉,最多X1張牌正面朝上了,最少也是X1張。
如果第二次只翻轉1張,再設1<X1<m 則最多會有X1+1張牌正面朝上,最少會有X1-1張牌正面朝上。
現在假設我們已經翻好k-1次
最多a張正面朝上,最少b張正面朝上
如果b>=Xk 則翻轉後最少有b-Xk張正面朝上(其餘的情況可以一次翻一張正面的,一張反面的,情況保持不變)。
否則,如果a>Xk,這時最少會有0張或1張正面朝上(根據a和Xk的奇偶性判斷),
再否則,最少會有Ak-a張正面朝上(先把正面朝上的全部翻轉,剩下的再翻便是最少張數)
關於最大值的討論同理
最後關於組合數的計算,由於m固定不變,就可以根據組合數公式C(m,n)=C(m-1,n)*(n-m+1)/m 線性求解,由於1e9+9是大素數,除法操作可用逆元求解代替。
<span style="font-size:14px;">#include <cstdio>#define LL long longint n,m;LL mod=1000000009;LL a[100005];void egcd(LL a,LL b,LL &x,LL &y){ if (b==0) { x=1;y=0; return ; } egcd(b,a%b,x,y); LL t=x; x=y,y=t-a/b*y; return;}LL cal(LL x,LL y){ LL cur=1,tmp=0,t=0; while(t<=y) { if (t>=x&&t<=y) if ((t-x)%2==0) tmp=(tmp+cur)%mod; t++; cur=cur*(m-t+1)%mod; LL t1,t2; egcd(t,mod,t1,t2); t1=(t1+mod)%mod; cur=cur*t1%mod; } return tmp;}int main(){ while (~scanf("%d%d",&n,&m)){ LL upper,lower,plower,pupper; for (int i=1;i<=n;i++) scanf("%I64d",&a[i]); upper=lower=pupper=plower=0; for (int i=1;i<=n;i++) { if (plower>=a[i]) lower-=a[i]; else if (pupper>=a[i]) lower=0+((pupper+a[i])%2==1); else lower=a[i]-pupper; if (pupper+a[i]<=m) upper+=a[i]; else if (plower+a[i]<=m) upper=m-((plower+a[i])%2==1); else {upper=m-(plower+a[i]-m);} plower=lower;//plower代表上一步的最小值 pupper=upper;//pupper代表上一步的最大值 } printf("%I64d\n",cal(lower,upper)); } return 0;}</span>