hdoj 4869 Turn the pokers 【组合数 + 逆元】【费马小定理——>逆元】



Turn the pokers

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1898    Accepted Submission(s): 671


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).
 

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 3
Hint
For 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)
 


表示看了一天了。。。 (⊙o⊙)   



题意:有m张牌,1代表牌是面朝上的,0代表牌是面朝下的,初始默认牌都是朝下的。

现在有n次操作,每次操作都会翻转数目为x(1 <= x <= m)的牌这也意味着要改变它的状态。

问你n次操作后,m张牌的最后状态有多少种?



逆元:由费马小定理a ^ (p-1) = 1 (%p),两边同时除去a——a ^ (p-2) = a^-1 (%p),得a的逆元a^-1 = a ^ (p-2)。


大牛题解:点我


奇偶性真是好东西。。。


AC代码:


#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 100000+10
#define MOD 1000000009
#define LL long long
using namespace std;
LL pow_mod(LL a, LL n)//费马小定理 求解逆元
{
    LL ans = 1;
    while(n)
    {
        if(n & 1)
            ans = ans * a % MOD;
        a = a * a % MOD;
        n >>= 1;
    }
    return ans;
}
LL c[MAXN];
int main()
{
    int n, m;
    while(scanf("%d%d", &n, &m) != EOF)
    {
        int Min = 0, Max = 0;//最少1的个数 最多1的个数
        int t1, t2, x;
        for(int i = 1; i <= n; i++)
        {
            scanf("%d", &x);
            //求解最少的1的个数 每次最小化1的个数
            if(Min >= x)
                t1 = Min - x;
            else if(Max >= x)// Min <= x <= Max
                t1 = ((Min&1) == (x&1)?0:1);//奇偶性相同 可以达到最优的状态
                //t1 = min(m-x+Min, Max-x);
            else
                t1 = x - Max;
            //求解最多的1的个数 每次最大化1的个数
            if(m - Max >= x)
                t2 = Max + x;
            else if(m - Min >= x)
                t2 = (((Min+x)&1) == (m&1) ? m :m-1);//同上
                //t2 = max(2*m - x - Max, Min+x);
            else
                t2 = 2*m - x - Min;
            Min = t1, Max = t2;
        }
        //求解组合数
        LL ans = 0;
        c[0] = 1;
        if(Min == 0)//最少的1的个数为0
            ans = 1;
        for(int i = 1; i <= Max; i++)
        {
            if(2*i > m)
                c[i] = c[m-i];//组合数规律 m里面取i个 和取m-i个一样
            else
                c[i] = c[i-1] * (m-i+1) % MOD * pow_mod(i, MOD-2) % MOD;//利用逆元 求解
            if(Min <= i && (Min&1) == (i&1))
                ans += c[i], ans %= MOD;
        }
        printf("%lld\n", ans);
    }
    return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值