首页 > 代码库 > HDU 4869 Turn the pokers
HDU 4869 Turn the pokers
看完题解还wa了老半天才ac = =!囧rz 给大牛门跪了
因为本题求的是最后状态的种数,设最终翻到正面为1,反面为0:
因为每次翻牌的选择自由,所以一定范围内,翻到正面的牌数相差2的状态都可以取到;
(假设当前状态为i 个1,可以用一次翻到1的机会把另一个翻到1的牌翻到0;这样状态就是i-2,相差2了;同理可得i+2(设i,i-2,i+2都在范围内)
接下来就是求这个范围(1的个数)了:
设l 为范围的下界,r 为范围的上界;nl 为当前下界,nl 为当前上界;
当前可翻牌数为x ;
求下界:
若 x<=l :nl = l-x;l 为前一次翻完后1的个数的最小值,若x<l,当前最小值便是 l-x;(把1翻为0)
若 x>l && x<=r : nl = ( l &1 == x&1)?0:1;前一次翻完后状态可以为l,r 中间相差为2的所有值,所以当x与l,r相差为2的倍数时,为0,否则为1;
若 x>r :nl = x-r ; 若x比最大值还大,则把所有1翻为0后又要把 x-r个0翻为1;
求上界:
若 x+r<=m :nr=x+r;x加上最大值小于总数时,当前最大值就是 x+r;
若 x+r>m && x+l<=m:nr = (x+l )&1==m&1?m:m-1;前一次翻完后状态可以为l,r中间相差为2的所有值,所以当x+l 与m相差为2的倍数时,存在k,使x+k==m,否则只有 x+k==m-1,或x+k==m+1;
若 x+l>m :nr=m-(x+l-m)=m*2-(x+l);若x+l>m,则把所有0翻为1后还需把x+l-m个1翻为0;
知道范围后便是求所有状态的种数了,即组合排列问题;
一般来说,c(m,i)=c(m,i-1)*(m-i+1)/i;
但是本题为取模题;除法的取模运算并不像乘法一样简单;所以我们利用费马小定理来把除法取模转化为乘法:
费马小定理:a^(p-1)≡1(mod p); p为质数;
所以 a^(p-2)=a^-1 (mod p)
所以 原式中 /i 的部分可以转化为 *i^-1(mod p)=*i^(p-2) (mod p)
因为本题为模 1e9+9,是质数。。。所以p=1e9+9;
这部分可以用快速幂取模来得到;
所以 c(m,i)=c(m,i-1)*(m-i+1) *i^(p-2) ;
即 c(m,i)=c(m,i-1)*(m-i+1) %p *pow_mode (i,p-2) %p ;
ps:开始wa到不行,所以从大牛们博客里扒了些代码混杂自己的代码交上去a过一发 orz;发现错误后,看懂题解后自己又写了一发交上去了;
这是自己写的,居然也还改了两三次 囧rz
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace std; 5 6 typedef long long ll; 7 8 const ll mod = 1e9+9; 9 10 ll pow_mode (ll a,ll b){11 ll temp=a;12 ll s=1;13 while (b){14 if (b&1)15 s=(s*temp)%mod;16 b/=2;17 temp=(temp*temp)%mod;18 }19 return s;20 }21 22 int main (){23 ll n,m;24 ll c[100005];25 while (cin>>n>>m){26 ll x,ans;27 ll l,r,nl,nr;28 l=r=nl=nr=0;29 while (n--){30 cin>>x;31 if (x<=l) nl=l-x;32 else if (x<=r) nl=(x&1)==(l&1)?0:1;33 else nl=x-r;34 if (r+x<=m) nr=r+x;35 else if (l+x<=m) nr=((l+x)&1)==(m&1)?m:m-1;36 else nr=m*2-(l+x);37 l=nl;r=nr;38 }//cout<<l<<" "<<r<<endl;39 c[0]=1;40 ans=0;41 for (int i=0;i<=r;i++){42 if (i){43 if (i*2<=m)44 c[i]=((c[i-1]*(m-i+1))%mod*pow_mode (i,mod-2))%mod;45 else c[i]=c[m-i];46 }47 if ((i&1)==(l&1)&&i>=l)48 ans=(ans+c[i])%mod;49 }50 cout<<ans%mod<<endl;51 }52 return 0;53 }