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\n",ans); 66 } 67 return 0; 68 }//by tkys_Austin;
(快省选了,关于扩lucas,扩gcd,扩CRT的叙述后面有时间可能会补上)