[AH2017/HNOI2017]抛硬币 题解

这道题有神奇的多组数据, 沙雕的输出方式, 所以这里改下题面(当然把改过的题的做法稍稍修改下就可以AC原题)。

改过的题面:
给定\(a、b\) \(\ (1 \leq a,b \leq 10^{15}, b \leq a \leq b +10000)\)
求两个人\(A、B\)分别抛\(a、b\)次硬币, \(A\)抛出硬币正面朝上的总次数大于\(B\)抛出硬币正面朝上的总次数的情况有多少种, 答案对\(10^9\)取模。


说明

  • 下文中\(W_A\)表示一种特定情况中\(A\)所抛硬币正面朝上的总次数, \(W_B\)类似。
  • 下文中将一种特定情况看作两个分别长\(a、b\)\(01序列\) \(s_A、s_B\), 其中\(s_A\)代表\(A\)抛硬币的具体情况; 序列中的\(1\)表示正面朝上, \(0\)表示反面朝上。

先考虑\(a=b\)的情况。

考虑一个\(A\)胜利的情况, 此时将\(a=b, W_A>W_B\)
\(s_A、s_B\)分别取反后, \(W_A = a-W_A, W_B = b-W_B\), 此时
\(W_A < W_B\), 取反后的\(s_A、 s_B\)对应着一种\(A\)败北的情况。

注意到一种\(A\)胜利的情况对应着唯一 一种\(A\)败北的情况且每一种\(A\)胜利的情况不会对应着同一种\(A\)败北的情况, 于是可以发现\(A\)胜利的情况数与\(A\)败北的情况数等值。

考虑\(A、B\)平手的情况数, 容易得出是\(\sum_{i=0}^a C_a^i C_a^i\), 套用范德蒙德卷积, 可以化简为\(C_{2a}^a\)

结合 \(所有情况数 = A胜利情况数 + A败北情况数 + 平手情况数\)

可以算出\(a=b\)时的答案为

\[\frac{2^{a+b} - C_{2a}^a}2 \tag{% $10^9$} \]

最后又一个要注意的地方, 就是\(2\)在膜\(10^9\)意义下是没有逆元的, 这时候就要优化一下式子。

首先将其拆开 :

\[\frac{2^{a+b}}2 - \frac{C_{2a}^a}2 \]

\[=2^{a+b-1} - \frac{C_{2a}^a}2 \]

手玩一下杨辉三角就可以发现\(C_{2a}^a\)必定是偶数, 并且
通过\(C_i^j = C_{i-1}^j + C_{i-1}^{j-1}\)算出\(C_{2a}^a\)时, \(C_{i-1}^j\)\(C_{i-1}^{j-1}\)也是相同的,
\(C_{2a}^a = 2*C_{2a-1}^a = 2*C_{2a-1}^{a-1}\)

化简到这里就可以套\(exLucas\)算了。


再考虑\(a>b\)的情况。

考虑\(A\)胜利的一种情况, 由于\(a>b, W_A>W_B\), 所以此时将\(s_A、s_B\)分别取反后并不一定出现\(W_A < W_B\)的情况。

但是如果是\(A\)败北或平局的情况, 由于\(a>b\)\(W_A \leq W_B\), 则此时将\(s_A 、s_B\)分别取反后, 一定有\(W_A > W_B\)

由此可以推出, 每个\(A\)不胜的情况, 将其\(s_A、s_B\)取反后,一定唯一对应一种\(A\)胜的情况。

再由于

\[总情况数 = A不胜的情况数 + 与不胜情况有关的A胜的情况数 + 与不胜情况无关的A胜的情况数 \]

(其中\(A不胜的情况数 = 与不胜情况有关的A胜的情况数\)

再由于\(总数\)为偶数, 故最后要算的就是:

\[\frac{2^{a+b} + 与不胜情况无关的A胜的情况数 }{2} \]

继续考虑如何计算 \(与不胜情况无关的A胜的情况数\)
发现如果一种\(A\)胜的情况与\(A\)不胜的情况无关, 那么其满足

\[a > b, W_A>W_B, a-W_A > b-W_B \]

整理得

\[\begin{cases} a-b \geq 1\\ W_A-W_B \geq 1 \\ W_A-W_B \leq a - b - 1 \end{cases} \]

然后就可以枚举\(W_A - W_B\)\(W_B\)的值, 计算

\[\sum_{i=1}^{a-b-1} \sum_{j=0}^b C_a^{i+j} C_b^{j} \]

化简一下

\[\sum_{i=1}^{a-b-1} \sum_{j=0}^b C_a^{i+j} C_b^{b-j} \]

\[=\sum_{i=1}^{a-b-1} C_{a+b}^{b+i} \]

\[=\sum_{i=b+1}^{a-1} C_{a+b}^{i} \]

所以答案就是

\[\frac{2^{a+b} + \sum_{i=b+1}^{a-1} C_{a+b}^{i} }{2} \]

然后\(\sum_{i=b+1}^{a-1} C_{a+b}^{i}\)要怎么除以二呢?
注意到\(a+b-(b-1) = a-1\), 结合\(C_{n+m}^m = C_{n+m}^n\), 可以发现
对于\(\sum_{i=b+1}^{a-1} C_{a+b}^{i}\)这个式子的每一个\(i\),都有\(a+b-i\)使得\(C_{a+b}^i = C_{a+b}^{a+b-i}\), 然后就可以除以2了。

但是注意到\(a+b\)为偶数的时候, \((a-1)-(b+1)+1\)是奇数,
怎么办?
研究了偶数所对的行在杨辉三角的构造后, 发现那个“处于中间的独身者”正好是\(C_{a+b}^{(a+b)/2}\), 于是就可以套用\(C_{2a}^a = 2*C_{2a-1}^a = 2*C_{2a-1}^{a-1}\), 于是就算完了。


这道题卡常, \(exLucas\)要写得精巧些。(太棒了, 学到许多

Luogu&loj 数据AC代码:

#include<bits/stdc++.h>
using namespace std;
#define li long long
const int mod = 1000000000ll;
const li p1 = 512ll;
const li p2 = 1953125ll;
li ksm(li a, li b, li p) {
	li res = 1ll;
	for(;b;b>>=1, a=(a*a)%p)
		if(b&1) res = (res*a)%p;
	return res%p;
}
void exgcd(li a, li b, li &x, li &y) {
	!b ? x=1,y=0 : (exgcd(b,a%b,y,x), y-=x*(a/b));
}
li inv(li a, li p) {
	li x, y;
	// ax + py = 1;
	exgcd(a,p,x,y);
	x = ((x%p+p)%p);
	return x;
}

li TP2[605], TP5[2000005];
li fac(li n, li p, li pk) {
	if(!n) return 1ll;
	li res = p==2 ? TP2[pk] : TP5[pk];
	res = ksm(res, n/pk, pk);
	res *= p==2 ? TP2[n%pk] : TP5[n%pk];
	res %= pk;
	return fac(n/p,p,pk) * res % pk;
}
li C(li n, li m, li p, li pk) {
	if(!m) return 1ll;
	li cnt = 0ll;
	for(li i=n;i;i/=p) cnt += i/p;
	for(li i=m;i;i/=p) cnt -= i/p;
	for(li i=n-m;i;i/=p) cnt -= i/p;
	if(cnt > 9) return 0ll;
	li f1 = fac(n,p,pk), f2 = fac(m,p,pk), f3 = fac(n-m,p,pk);
	return f1 * inv(f2,pk) % pk * inv(f3,pk) % pk * ksm(p,cnt,pk) % pk;
}
li a1, a2;
li exlucas(li n, li m) {
	a1 = C(n,m,2ll,p1);
	a2 = C(n,m,5ll,p2);
	li res = 0ll;
	res += a1 * p2 % mod * inv(p2,p1) % mod;
	res %= mod;
	res += a2 * p1 % mod * inv(p1,p2) % mod;
	res %= mod;
	res = ((res%mod+mod)%mod);
	return res;
}

void PRint(li ans, li k) {
	li tmp = 100000000, num = 9;
	while(tmp)
	{
		if(num<=k)
			cout << ans/tmp;
		ans %= tmp;
		tmp /= 10;
		--num;
	}
	putchar('\n');
}
int main()
{
	TP2[0] = TP5[0] = TP2[1] = TP5[1] = 1ll;
	for(li i=1; i<=p1; ++i)
		TP2[i] = i%2 ? TP2[i-1]*i%p1 : TP2[i-1];
	for(li i=1; i<=p2; ++i)
		TP5[i] = i%5 ? TP5[i-1]*i%p2 : TP5[i-1];
	li a, b, k;
	while(scanf("%lld%lld%lld", &a, &b, &k) == 3) {
		li ans = ksm(2,a+b-1,mod);
		if(a==b) {
			ans -= exlucas(2*a-1, a);
			ans %= mod;
		} else {
			if((a+b)%2 == 0) {
				for(li i=b+1; i<(a+b)/2; ++i) {
					ans += exlucas(a+b,i);
					ans %= mod;
				}
				ans += exlucas(a+b-1, (a+b)/2);
				ans %= mod;
			} else {
				for(li i=b+1; i<=(a+b)/2; ++i) {
					ans += exlucas(a+b,i);
					ans %= mod;
				}
			}
		}
		ans = (ans%mod+mod)%mod;
		PRint(ans, k);
	}
	return 0;
}
posted @ 2020-04-20 16:49  xwmwr  阅读(191)  评论(0编辑  收藏  举报