hdu4689 Turn the pokers 组合

10 篇文章 0 订阅

    m张相同的扑克牌,n个操作,每次操作可以选择反转其中任意Xi张,问n次操作完后,这m张扑克牌有多少种状态。虽然题意看的有点蒙,不过hint还是把要做的事解释的挺清楚了。最后的状态数只和正面(或者反面)的牌的数量有关,所以只要求出来最后m张牌中,可能的正面朝上的牌的数量{a1,a2,a3...ak},那么答案就是C(m,a1)+C(m,a2)+...+C(m,ak)了。现在的问题就是求可能的正面牌的数量。假如现在要翻转奇数张牌,那么翻牌方案一定是奇数个正面+偶数个反面,偶数个正面+奇数个反面,翻转后,正面牌数的奇偶性交换;如果要翻转偶数张牌,则一定是奇数+奇数或者偶数+偶数,翻转后奇偶性不变,这样可以根据序列X,判断出翻转完成后,正面牌德数量是奇还是偶。再举一个例子,假设有8张牌,其中5张正面(+)3张背面(-),如果翻转3张,那么可以翻0+3-,1+2-,2+1-,3+0-,结果正面的数量分别是8,6,4,2,进而可以发现,由于翻转的牌是任意的,所以翻转后所有可能的正面牌的数量一定是连续的几个奇数或连续的几个偶数。那么剩下的就是根据序列X,维护正面牌数量的区间了。假设当前的区间为(l,r),对于此次操作Xi,首先讨论l,若l>Xi,则显然l=l-Xi,否则,考虑区间右端r,若r>Xi,则Xi在区间中,显然l=0.否则最优方案就是把r张背面牌全部翻转,之后再把Xi-r张牌翻转,得到一个最小l,类似的可以得出新的区间r.感觉这里写的复杂了,应该还有更简单的递推方法.........

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <queue>
using namespace std;
typedef long long ll;
const ll mod=1000000009;

ll pow_mod(ll x,ll y)
{
    ll res=1;
    while(y)
    {
        if (y&1) res*=x,res%=mod;
        x=x*x;
        x%=mod;
        y>>=1;
    }
    return res%mod;
}
ll cc[105000];
ll C(ll x,ll y)
{
    if (y==0 || y==x) return 1ll;
    ll fm=cc[x-y]*cc[y]%mod;
    ll res=cc[x]*pow_mod(fm,mod-2);
    return res;
}
int a[120000];
int n,m;
int main()
{
//    freopen("in.txt","r",stdin);
    cc[1]=1;
    for (int i=2; i<=100000; i++)
    cc[i]=cc[i-1]*i,cc[i]%=mod;

    while(~scanf("%d%d",&n,&m))
    {
        int l,r=0;
        for (int i=0; i<n; i++)
        {
            scanf("%d",&a[i]);
        }
        l=r=a[0];
        int odd=0;
        if (l&1) odd=1;
        int ol=l;
        for (int i=1; i<n; i++)
        {
            if (l>=a[i]) l-=a[i];
            else
            {
                if(r>=a[i]) l=0;
                else l=a[i]-r;
            }

            if (m-r>=a[i]) r+=a[i];
            else
            {
                if (ol+a[i]<=m) r=m;
                else r=m-(a[i]-(m-ol));
            }
            ol=l;
            if (a[i]&1) odd^=1;
        }
        ll ans=0;
        if (odd)
        {
            if (l%2==0) l++;
            if (r%2==0) r--;
            for (int i=l; i<=r; i+=2)
            {
                ans+=C(m,i);
                ans%=mod;
            }
        }
        else
        {
            if (l&1) l++;
            if (r&1) r--;
            for (int i=l; i<=r; i+=2)
            {
                ans+=C(m,i);
                ans%=mod;
            }
        }
        printf("%I64d\n",ans);

    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值