hdu 4869 Turn the pokers
Turn the pokers
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 731 Accepted Submission(s): 254
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).
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 3HintFor 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)
Author
FZU
Source
题解:官方给出的题解看了半天看懂了一部分。后来看了别人的博客。讲的挺具体可是有的地方还是非常难懂,和朋友讨论了半天最终弄懂了全部的细节。
首先。我们把牌的正反面的情况规定成0,1的情况(0反,1正)。进行m次操作,共翻转s=sum(x0+x1+....+xm-1)次,最后出现1的数目必定和s同奇同偶(s为奇数,1的个数就为奇数,反之为偶数)----每翻转一张牌(把1翻转成0,0翻转成1)。1的个数都发生奇偶变化。開始时没翻转。也就是0次翻转,1的个数为偶数。翻转1次变奇数。再翻转一次变偶数.....依次类推,以上得证。
那么这样我们就能找出最后1的个数的最小数目l和最大数目r(l,r与s同奇偶),中间过程中我们把某两次从0翻转成1的情况拿出来。翻转同一张牌(相当于相互抵销)。那我们最后1的个数就会多2或少2。这样我们最后1的个数就是l。l+2,.....。r-2,r的等差数列,然后用求出c(m,i)的和就能够了。所以主要任务就是求出l和r。代码中有凝视具体解释。
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using namespace std; typedef __int64 LL; static const int mod = 1000000009; LL quick_mod(LL a,LL b) { LL t=1; while(b) { if(b&1) t=t*a%mod; b/=2; a=a*a%mod; } return t; } int main() { LL m,n,x; while(cin>>m>>n) { LL l=0,r=0,p=0,q=0; //l用来记录前一状态1的最小数,r为最大数 //p用来记录当前状态1的最小数,q为最大数 for(LL i=0;i<m;i++) { cin>>x; //求取最少1的数目,有1翻1 if(l-x>=0) p=l-x; //1)当前状态1的最少数目为l,挑选x张牌翻转, // 当l>=x,也就是说我能把当中随意x张1翻转成0 else if(r-x>=0) p=(r-x)%2; //2)x>l&&x<=r,因为当前状态1的个数最大为r,最小为l, //其次我能够通过前面的情况构造[l,r]中与l同奇偶数 //所以我仅仅要构造出与x离得近期的那个就能够使得p仅仅需i小 else p=x-r; //3)x大于当前状态1的最大数目,也就是我除了把这r个1翻转成0 //还须要把x-r个0翻转成1,最小数目为x-r //求取最多1的数目。有0翻0 if(r+x<=n) q=r+x; //与上面类似,不再赘述 else if(l+x<=n) q=n-((n-l)%2!=x%2); else q=2*n-(l+x); l=p,r=q; } LL c[100010],ans=0; //使用c数组记录c(m,i)%mod c[0]=1; if(l==0) ans+=1; for(LL i=1;i<=r;i++) { if(n-i<i) c[i]=c[n-i]; else c[i]=c[i-1]*(n-i+1)%mod*quick_mod(i,mod-2)%mod; //这里使用了c(m,i)=c(m,i-1)*(m-i+1)/i的求法 //因为过程中须要取余。所以不能直接用公式计算 //当除以一个数k同一时候对p取余时,能够把除法转化成乘以这个数k对于p的逆元来计算 //k对于p的逆元的求法就是k*x≡1(mod)p,全部满足上述条件的x都是一个逆元 //因为p是素数,依据费马小定理k^(p-1)≡1(mod)p //这里进行一下分解k*k^(p-2)≡1(mod)p,可看出k^(p-2)是k对于p的一个逆元 if((l%2==i%2)&&i>=l) ans=(ans+c[i])%mod; } cout<<ans<<endl; } return 0; }
版权声明:本文博客原创文章。博客,未经同意,不得转载。