列表

详情


NC20121. [HNOI2017]抛硬币

描述

小 A 和小 B 是一对好朋友,他们经常一起愉快的玩耍。最近小 B 沉迷于**师手游,天天刷本,根本无心搞学习。但是已经入坑了几个月,却一次都没有抽到 SSR,让他非常怀疑人生。勤勉的小 A 为了劝说小 B 早日脱坑,认真学习,决定以抛硬币的形式让小 B 明白他是一个彻彻底底的非洲人,从而对这个游戏绝望。两个人同时抛 b 次硬币,如果小 A 的正面朝上的次数大于小 B 正面朝上的次数,则小 A 获胜。
但事实上,小 A 也曾经沉迷过拉拉游戏,而且他一次 UR 也没有抽到过,所以他对于自己的运气也没有太大把握。所以他决定在小 B 没注意的时候作弊,悄悄地多抛几次硬币,当然,为了不让小 B 怀疑,他不会抛太多次。现在小 A 想问你,在多少种可能的情况下,他能够胜过小 B 呢?由于答案可能太大,所以你只需要输出答案在十进制表示下的最后 k 位即可。

输入描述

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

输出描述

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

示例1

输入:

2 1 9
3 2 1

输出:

000000004
6

说明:

【样例解释】
对于第一组数据,当小A抛2次硬币,小B抛1次硬币时,共有4种方案使得小A正面朝上的
次数比小B多。(01,0),(10,0),(11,0),(11,1)
对于第二组数据,当小A抛3次硬币,小B抛2次硬币时,共有16种方案使得小A正面朝上的次数比小B多。(001,00),
(010,00),(100,00),(011,00),(101,00),(110,00),(111,00),(011,01),(101,01),(110,01),(111,01),(011,10),
(101,10),(110,10),(111,10),(111,11)

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

C++ 解法, 执行用时: 165ms, 内存消耗: 15736K, 提交时间: 2021-05-21 17:52:16

#include<cstdio>
using namespace std;
typedef long long ll;
ll v[3], pri[3], xinv[3], fac[6][2000000];
inline ll qp(ll a, ll x, ll mod){
    ll i = 1;
    for(;x;x >>= 1, a = a * a % mod)if(x & 1)i = i * a % mod;
    return i;
}
inline ll cal(ll n, ll p, ll pr){
	return n ? qp(fac[p][pr - 1], n / pr, pr) * fac[p][n % pr] % pr * cal(n / p, p, pr) % pr : 1;
}
inline void exgcd(ll a, ll b, ll &x, ll &y, ll p){
	if(!b)x = 1, y = 0;
	else{
		exgcd(b, a % b, y, x, p);
		y -= a / b * x;
	}
}
inline ll getinv(ll a, ll p){
	ll x, y;
	if(!a)return 0;
	exgcd(a, p, x, y, p);
	return (x % p + p) % p;
}
inline ll getfac(ll n, ll m, ll p, ll pr){
	if(n < m)return 0;
	ll i, j;
	for(i = n, j = 0;i;i /= p)j += i / p;
	for(i = m;i;i /= p)j -= i / p;
	for(i = n - m;i;i /= p)j -= i / p;
	if(j >= 9)return 0;
	ll x = cal(n, p, pr), y = cal(m, p, pr), z = cal(n - m, p, pr);
	return qp(p, j, pr) * x % pr * getinv(y, pr) % pr * getinv(z, pr) % pr;
}
inline ll lucas(ll n, ll m, ll p){
	ll i, j;
	if(n < m)return 0;
	for(i = 1, j = 0;i <= v[0];i++)j = (j + xinv[i] * getfac(n, m, pri[i], v[i]) % p) % p;
	return j;
}
int main(){
	ll a, b, k, p, i;
	v[0] = 2;pri[1] = 2;pri[2] = 5;
	for(k = 1;k <= 2;k++)
		for(i = fac[pri[k]][0] = 1, v[k] = qp(pri[k], 9, 2e9);i < v[k];i++){
			fac[pri[k]][i] = fac[pri[k]][i - 1];
			if(i % pri[k])fac[pri[k]][i] = fac[pri[k]][i] * i % v[k];
		}
	while(~scanf("%lld%lld%lld", &a, &b, &k)){
		for(i = 1;i <= 2;i++)v[i] = qp(pri[i], k, 2e9);
		for(p = v[i = 1] * v[2];i <= 2;i++)xinv[i] = v[3 - i] * getinv(v[3 - i], v[i]) % p;
		k = qp(2, a + b - 1, p);
		if(a == b)k = (k - lucas(a + b - 1, a, p) + p) % p;
		else{
			for(i = b + 1;i <= (a + b - 1) / 2;i++)k = (k + lucas(a + b, i, p)) % p;
			if(a + b & 1 ^ 1)k = (k + lucas(a + b - 1, a + b >> 1, p)) % p;
		}
		while(k < p / 10)putchar('0'), p /= 10;
        printf("%lld\n", k);
	}return 0;
}

上一题