首页 > 代码库 > 2014多校第一场 I 题 || HDU 4869 Turn the pokers(费马小定理+快速幂模)

2014多校第一场 I 题 || HDU 4869 Turn the pokers(费马小定理+快速幂模)

题目链接

题意 : m张牌,可以翻n次,每次翻xi张牌,问最后能得到多少种形态。

思路 :0定义为反面,1定义为正面,(一开始都是反), 对于每次翻牌操作,我们定义两个边界lb,rb,代表每次中1最少时最少的个数,rb代表1最多时的个数。一张牌翻两次和两张牌翻一次 得到的奇偶性相同,所以结果中lb和最多的rb的奇偶性相同。如果找到了lb和rb,那么,介于这两个数之间且与这两个数奇偶性相同的数均可取到,然后在这个区间内求组合数相加(若lb=3,rb=7,则3,5,7这些情况都能取到,也就是说最后的结果就是在所有的牌中选3张翻过去+选5张+选7张)。所以,要根据情况先求出边界。

由于数据相当大,所以要将组合中的除法变成乘法,C(n, m) = n!/(m!*(n-m)!),由 费马小定理:若p是质数 , a^(p-1) = 1%p,那么,a^(p-2) = 1/a%p,利用这个公式,得到1/(m!*(n-m)!) = (m!*(n-m)!)^(p-2) mod p,即C(n, m) = n!*(m!*(n-m)!)^(p-2) mod p,这样就可以变除为乘。而 求(n-m)!)^(p-2 )mod p中用快速幂简化运算。

here这个人的代码对于边界解释的较为详尽。

官方题解 :

最终的结果一定是连续出现的,只需要求出最终的区间。

因为如果对同一张牌进行两次操作,牌的状态不改变。故牌的翻转次数一定是减少偶数次。如果所有数的和是奇数,那么最终结果也一定是奇数。同理,偶数也是一样的。

所以只要递推求出最后的区间,计算sumCxim)(i=012。。。)),m是总牌数,xi是在区间内连续的奇数或偶数,在模10^9+9就是最终的答案。

 

 1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4  5 using namespace std ; 6  7 #define mod 1000000009 8 #define LL __int64 9 10 LL f[100010] ;11 void mul()12 {13     f[0] = 1 ;14     for(int i = 1 ; i < 100010 ; i++)15         f[i] = (f[i-1] * i)%mod ;16 }17 18 LL multimod(LL a,LL b)19 {20     LL res = 1 ;21     while(b)22     {23         if(b & 1)24         {25             res *= a ;26             res %= mod ;27         }28         b >>= 1 ;29         a *= a ;30         a %= mod ;31     }32     return res ;33 }34 int main()35 {36     int n , m ;37     mul() ;38     while(scanf("%d %d",&n,&m) != EOF)39     {40         int x ;41         scanf("%d",&x) ;42         int lb = x, rb = x ;43         for(int i = 1 ; i < n ; i++)44         {45             scanf("%d",&x) ;46             int l = lb - x ;47             int r = rb + x ;48             if(l < 0) {49                 if(rb - x <= 0 ) l = x - rb ;50                 else l =  ( (lb - x)%2 + 2)%2 ;//类似于奇数张牌中翻奇数张最后肯定是偶数,奇数翻偶数张肯定是奇数,肯定会有一个中间状态得到最后非0即151             //此时,正处于lb<x<rb的时候,当翻x张牌时,若x的奇偶性与lb,rb相同时,则代表刚好是翻牌已经达到的某个状态,也就是说把剩下的x张都反过来即可,而奇偶若是不同,说明要么多一张或者是少一张,所以就是1.52             }53             if(r > m){54                 if(lb + x <= m) r = m - (rb + x)%2;55                 else r = m-(lb+x - m) ;56             }57             lb = l ;58             rb = r ;59         }60         LL ans = 0 ;61         for(int i = lb ; i <= rb ; i += 2)62             ans += ((f[m] % mod) * (multimod((f[i]*f[m-i]) % mod,mod-2)))%mod ;63         cout << ans % mod << endl ;64     }65     return 0 ;66 }
View Code