首页 > 代码库 > hdu 4869 Turn the pokers
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
2014 Multi-University Training Contest 1
题解:官方给出的题解看了半天看懂了一部分,后来看了别人的博客,讲的挺详细但是有的地方还是很难懂,和朋友讨论了半天终于弄懂了所有的细节。
首先,我们把牌的正反面的情况规定成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; }
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。