zoukankan      html  css  js  c++  java
  • HDOJ--4869--Turn the pokers【组合数学+高速幂】

    链接:http://acm.hdu.edu.cn/showproblem.php?pid=4869

    题意:有m张扑克。開始时所有正面朝下。你能够翻n次牌,每次能够翻xi张。翻拍规则就是正面朝下变背面朝下,反之亦然,问经过n次翻牌后牌的朝向有多少种情况。

    这道题在比赛时我们仅仅开了个头。却无从下手。

    我看了网上的解题报告。说的都比較简单,对于我这名菜鸟来说也想了比較长的时间才想明确,所以我想写的清晰一些日后再看还能看的非常清晰。

    思路是这样,每张牌翻奇数次必定是正面朝上。翻偶数次则还是正面朝下。如今用0表示初始状态(正面朝下),1表示正面朝上,能够依据n次翻牌的个数找出1的下限和上限。然后再在这个范围里用组合数学就ok了。比赛时就是想到了这里,找范围没有想出来。

    这道题能够分为两部分:先是找到1的上限和下限,然后是计算出 c ( m , i ) 的值依次相加。


    第一部分:

            minm = maxm = 0;
            p = q = 0;
            for(i=0;i<n;i++){
                scanf("%d",&x);
                if(minm>=x)        p = minm - x;                        //一
                else if(maxm>=x)   p = ((x&1)==(minm&1))?0:1;           //二
                else               p = x - maxm;                        //三
                if(maxm+x<=m)      q = maxm + x;                        //四
                else if(minm+x<=m) q = (((minm+x)&1)==(m&1))?

    m:m-1; //五 else q = 2 * m - (x + minm); //六 minm = p; maxm = q; }

    minm表示下限,maxm表示上限,p、q分别记录当前上下限,然后更新到minm、maxm中。x是输入的第i次翻牌的数量

    第一组if语句是推断下限的。

    ①:当前下限大于等于如今翻牌的数量,这个比較好理解。全翻1,1变成0,则剩下的1就是minm-x

    ②:当前下限小于翻牌数量。上限大于等于翻牌数量。由于翻牌数量刚好在上下限之间,所以最少能够把正面朝上的数量减为零,但不是绝对能减到0,由于有可能当前正面朝上的牌时奇数。而翻牌数量是偶数,所以要推断奇偶性是否一样。为什么要和minm比較奇偶性。后面会说。

    ③:翻牌数量比上限还大的时候,直接减去上限就是下限。也不难理解

    ④:上限+翻牌数量没有达到总牌数时,上限+翻牌数量就是新的上限。全翻0,这样使1最多

    ⑤:上限+翻牌数量大于总牌数,而下限+翻牌数量小于等于总牌数,前者能够说是翻牌溢出了,已经全是1再翻的话仅仅会让一些1变成0,后者没有达到全变成1的情况。

    它们是一个上限一个下限。这说明能够处理到在这之间的情况,那么最好的结果是全部牌都正面朝上,全是1。和②的情况一样。须要推断奇偶性是否一致。这回和m比較。应该比②的好理解

    ⑥:上限+翻牌数、下限+翻牌数全都大于总牌数时。说明都会溢出,那就用2 * m - (x + minm)来表示上限。由于(x+minm)小。所以溢出的1变成0的牌数少。我之前用max(maxm + x - m, 2 * m - (x + minm))来表示⑥的上限,结果WA了。事实上是我想错了,maxm + x - m是1变成0的牌的数量。而要找的是1的上限。


    第二部分:

            c[0] = 1;
            for(i=1;i<=maxm;i++){
                if(m-i<i)   c[i] = c[m-i];
                else{
                    c[i] = c[i-1] * (m-i+1) % MOD * mode((ll)i,MOD-2) % MOD;
                }
            }

    当中mode是高速幂取模函数,数组c表示组合数学 c ( m , i ) , 按理说 c[ i ] = c[ i - 1 ] * ( m - i + 1 ) / i ,然后这个数对MOD取模,可是存在除法取模就不是这么简单的分解了,曾经做数论题应该遇到过。仅仅只是太久没做给忘了。。

    费马小定理是这样: a^(p-1) ≡1(mod p),p为质数。a、p互质。a^(p-1) mod p 恒等于1。

    变换一下,两边同一时候除以a 。变成 a^(p-2)=a^(-1)(mod p),所以要除以a 就能够表示成 乘 a^(p-2)。所以有了如上的写法。


    最后将组合数学值相加的时候。要隔一个相加,不难发现上限和下限的奇偶性一样,而且每种结果1的数量的奇偶性一定和上限下限的奇偶性一样,这个能够自己推

    我以列的方式表示牌的情况,O表示翻哪个牌,如果如今有7张牌。翻两次。第一次翻4张,第二次翻三张

    不论你怎么变化。每次翻完牌正面朝上的总是奇数个或偶数个,其它情况能够自己推,得到这个结论。

    如今说说第一部分的②,事实上应该和你如今更接近的那种情况去比較奇偶性,可是相比于某种中间情况,上下限的奇偶性更easy推断,并且中间每种情况的奇偶性都和上下限一样。所以能够x能够和minm比較奇偶性。当然。和maxm比較也能够,由于他们奇偶性一样。


    完整代码:

    #include<cstring>
    #include<string>
    #include<fstream>
    #include<iostream>
    #include<iomanip>
    #include<cstdio>
    #include<cctype>
    #include<algorithm>
    #include<queue>
    #include<map>
    #include<set>
    #include<vector>
    #include<stack>
    #include<ctime>
    #include<cstdlib>
    #include<functional>
    #include<cmath>
    using namespace std;
    #define PI acos(-1.0)
    #define MAXN 100100
    #define eps 1e-7
    #define MOD 1000000009
    #define INF 0x7FFFFFFF
    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1
    typedef long long ll;
    
    ll c[MAXN];
    ll mode(ll a,int n){
        ll t = a;
        ll ans = 1;
        while(n){
            if(n & 1){
                ans = ans * t % MOD;
            }
            n >>= 1;
            t =  t * t % MOD;
        }
         return ans;
    }
    int main(){
        int n,m,minm,maxm,i,j,x,p,q;
        while(scanf("%d%d",&n,&m)!=EOF){
            minm = maxm = 0;
            p = q = 0;
            for(i=0;i<n;i++){
                scanf("%d",&x);
                if(minm>=x)        p = minm - x;
                else if(maxm>=x)   p = ((x&1)==(minm&1))?0:1;
                else               p = x - maxm;
                if(maxm+x<=m)      q = maxm + x;
                else if(minm+x<=m) q = (((minm+x)&1)==(m&1))?

    m:m-1; else q = 2 * m - (x + minm); minm = p; maxm = q; } ll ans = 0LL; c[0] = 1; for(i=1;i<=maxm;i++){ if(m-i<i) c[i] = c[m-i]; else{ c[i] = c[i-1] * (m-i+1) % MOD * mode((ll)i,MOD-2) % MOD; } } for(i=minm;i<=maxm;i+=2){ ans += c[i]; ans %= MOD; } cout<<ans<<endl; } return 0; }



  • 相关阅读:
    自定义ProgressBar
    Anroid开发中常用快捷键
    Intent用法
    IO(Input Output)流
    自定义Dialog
    仿UC点击两次返回键退出程序
    横竖屏切换时Activity的生命周期
    单例模式
    Timer用法
    关于数组
  • 原文地址:https://www.cnblogs.com/blfshiye/p/5223232.html
Copyright © 2011-2022 走看看