bzoj4830 hnoi2017 抛硬币

题目描述

小 A 和小 B 是一对好朋友,他们经常一起愉快的玩耍。最近小 B 沉迷于**师手游,天天刷本,根本无心搞学习。但是已经入坑了几个月,却一次都没有抽到 SSR,让他非常怀疑人生。勤勉的小 A 为了劝说小 B 早日脱坑,认真学习,决定以抛硬币的形式让小 B 明白他是一个彻彻底底的非洲人,从而对这个游戏绝望。两个人同时抛 b 次硬币,如果小 A 的正面朝上的次数大于小 B 正面朝上的次数,则小 A 获胜。

但事实上,小 A 也曾经沉迷过拉拉游戏,而且他一次 UR 也没有抽到过,所以他对于自己的运气也没有太大把握。所以他决定在小 B 没注意的时候作弊,悄悄地多抛几次硬币,当然,为了不让小 B 怀疑,他不会抛太多次。现在小 A 想问你,在多少种可能的情况下,他能够胜过小 B 呢?由于答案可能太大,所以你只需要输出答案在十进制表示下的最后 k 位即可。

输入输出格式

输入格式:

 

有多组数据,对于每组数据输入三个数a,b,k,分别代表小A抛硬币的次数,小B抛硬币的次数,以及最终答案保留多少位整数。

 

输出格式:

 

对于每组数据,输出一个数,表示最终答案的最后 k 位为多少,若不足 k 位以 0 补全。

题意:

小A可以抛a次硬币,小B可以抛b次硬币(a>=b)问小A抛出正面的次数比小B多的情况种数,输出对10的k次方取余(k<=9);

题解:

①这是一个利用对应关系进行构造的组合问题:

如果每一种小A赢情况对应(把a,b次抛出的结果正面变成反面)一种小B赢的情况,那么总可能数/2就是答案,但是事实上不是,在可能会有小A无论在两种情况下都是比小B多,或者小A在两种情况下都小于等于小B的次数,为此,就只有分a=b和a>b讨论。

a=b

A和B掷出的正面相同,小A两种情况都赢不了

$F = sum_{i=0}^{a}C_{a}^{i}C_{a}^{i}=sum_{i=0}^{a}C_{a}^{i}C_{a}^{a-i} = C_{2a}^{a}$

(a选i个再在另外a个选a-i个和在2a个里选a个一一对应)

$ans = frac{2^{2a}-F}{2} = frac{2^{2a}-C_{2a}^{a}}{2}$

a>b

A和B掷出的正面满足A>B且a-A>b-B时小A两种情况都可以赢小B

$G = sum_{i=0}^{b}sum_{j=1}^{a-b-1}C_{b}^{i}C_{a}^{i+j} = sum_{i=0}^{b}sum_{j=1}^{a-b-1}C_{b}^{b-i}C_{a}^{i+j} = sum_{j=1}^{a-b-1}C_{a+b}^{j+b}$ 

(b个里选b-i个再在a个里选i+j个一一对应在a+b个里选b+j个)

$ans = frac{2^{2a}+G}{2} = frac{2^{2a}+sum_{j=1}^{a-b-1}C_{a+b}^{j+b}}{2}$

剩下的组合数取模用扩展lucas就好了,只是稍稍有点变化。

 

 1 #include<cstdio>
 2 #include<iostream>
 3 #define ll long long
 4 #define Maxn 1000000001
 5 #define RG register
 6 #define il inline
 7 using namespace std;
 8 ll a,b,K,mod,mod2,mod5,v[2][2500005];
 9 ll pw(ll x,ll y,ll Mod){
10     ll res = 1;
11     while(y){
12         if(y&1) res = res * x % Mod; 
13         y>>=1; x = x * x % Mod;
14     }
15     return res;
16 }
17 void init(ll k,ll mx){
18     ll typ = k!=2;
19     v[typ][0] = 1;
20     for(RG ll i = 1;i <= mx;i++){
21         if(i%k) v[typ][i] = v[typ][i - 1]*i % mx; 
22         else v[typ][i] = v[typ][i - 1];
23     }
24 }
25 inline void exgcd(ll a,ll b,ll &x,ll &y){
26     if(!b) {x = 1,y = 0;}
27     else exgcd(b,a%b,y,x),y -= a/b*x;
28 }
29 ll inv(ll a,ll p){
30     ll x,y; exgcd(a,p,x,y); 
31     return (x%p+p)%p;
32 }
33 il ll mul(ll n,ll p,ll pk){
34     if(!n) return 1;
35     ll ret = pw(v[p!=2][pk],n / pk,pk) * v[p!=2][n % pk] % pk;
36     return ret * mul(n / p,p,pk) % pk;
37 }
38 ll C(ll n,ll m,ll p,ll pk,ll fg){
39     if(n<m) return 0;
40     ll cnt = 0;
41     for(RG ll i = n;i;i/=p) cnt += i / p;
42     for(RG ll i = m;i;i/=p) cnt -= i / p;
43     for(RG ll i = (n - m);i;i/=p) cnt -= i / p;
44     if(p==2&&fg) cnt--; 
45     if(cnt>=K) return 0;
46     ll s1 = mul(n,p,pk),s2 = mul(m,p,pk),s3 = mul(n - m,p,pk); 
47     ll ret = pw(p,cnt,pk) * s1 % pk * inv(s2,pk) % pk * inv(s3,pk) % pk;
48     if(p==5&&fg) ret = ret * inv(2,pk) % pk;
49     return ret * (mod / pk) % mod * inv(mod / pk,pk) % mod;
50 }
51 ll lucas(ll n,ll m,ll fg) {return (C(n,m,2,mod2,fg) + C(n,m,5,mod5,fg)) % mod;}
52 int main()
53 {    //freopen("bzoj4830.in","r",stdin);
54     //freopen("bzoj4830.out","w",stdout);
55     init(2,512); init(5,1953125);
56     while(cin >> a >> b >> K){
57         mod2 = pw(2,K,Maxn); mod5 = pw(5,K,Maxn); mod = pw(10,K,Maxn);
58         ll ans = pw(2,a + b - 1,mod);
59         if(a==b) ans = (ans - lucas(a+b,a,1) + mod) % mod;
60         else {
61             for(RG ll i = (a+b)/2+1;i<a;i++) ans = (ans + lucas(a+b,i,0))%mod;
62             if(!((a+b)%2)) ans = (ans + lucas(a+b,(a+b)/2,1)) % mod; 
63         }
64         while(ans<mod/10) mod/=10,printf("0");
65         printf("%lld
",ans);
66     }
67     return 0;
68 }//by tkys_Austin;

(快省选了,关于扩lucas,扩gcd,扩CRT的叙述后面有时间可能会补上)

原文地址:https://www.cnblogs.com/Paul-Guderian/p/8647074.html