泰拳警告
- description
- solution
- code
description
题目描述
小七擅长泰拳,某天他打算与小枣切磋拳技,一共需要进行 n 次比赛。
由于双方拳技难分上下,每场比赛小七获胜或落败的概率都是 1/p+2 ,平局的概率是 p/p+2
若最后小七获胜场数大于落败场次,且平局次数为 k ,则能获得 k + 1 奖励分
小七想知道,他能获得的奖励分的期望是多少呢?为了避免精度误差,你需要输出答案在模 998244353 意义下的结果
输入格式
输入文件 fight.in 包含一行,输入两个正整数依次表示 n; p
输出格式
输出文件 fight.out 包含一行,仅一个非负整数,表示答案在模 998244353 意义下的结果
样例输入
2 1
样例输出
221832079
样例解释
两局都胜,或胜一局平一局是合法的,期望得分为 1/3 × 1/3 ×1+2× 1/3 × 1/3 ×2 = 5/9,在模 998244353 意义下为 221832079
数据范围及约定
测试点编号 | n | 特殊性质 |
---|---|---|
1 ∼ 4 | ≤ 2000 | 无 |
5 ∼ 8 | ≤ 105 | 无 |
9 ∼ 12 | ≤ 3 × 106 | p = 1 |
13 ∼ 20 | ≤ 3 × 106 | 无 |
对于 100% 的数据,满足 1 ≤ n ≤ 3 × 106; 1 ≤ p < 998244351 。可以证明答案一定能表示成一个既约分数 u/v,你需要找到满足 v × w ≡ 1 mod 998244353的自然数 w ,输出 u × w mod 998244353
solution
case 0
: 上来直接搞大DPDPDP,dpi,j:dp_{i,j}:dpi,j: 赢了iii场,输了jjj场的概率,自然就平了n−i−jn-i-jn−i−j场
枚举每一场是赢了还是输了甚至于平了,这是O(n3)O(n^3)O(n3)的,连最基础的测试点都不能拿到
case 1~4
:猜测改写dpi,j:dp_{i,j}:dpi,j: 在第iii轮为止平了jjj的概率,将胜场和负场合并在一起
dpi,j∗1p+2∗2→dpi+1,jdp_{i,j}*\frac{1}{p+2}*2\rightarrow dp_{i+1,j}dpi,j∗p+21∗2→dpi+1,j
dpi,j∗pp+2→dpi+1,j+1dp_{i,j}*\frac{p}{p+2}\rightarrow dp_{i+1,j+1}dpi,j∗p+2p→dpi+1,j+1
最后平了kkk场的方案数,如果胜了iii,负了jjj;则一定对应有一种胜了jjj,负了iii,看似/2/2/2就行了,总是恰好有一种胜场大于负场
但是如果i=j
,就都不能取,单独减掉
case 5~8
: n≤105n\le 10^5n≤105,没有特殊性质,应该是拿来给选手被卡log
的安慰
case 9~12
: p=1p=1p=1,意味着胜负平都是一样的概率,应该可以利用数学方法计算
case 13~20
:最后就是正解了,私以为还是p=1
的数据提供了可以数学计算的灵感
枚举平了kkk场,因为胜负的概率一样,所以平了kkk场的每种情况概率都是一样的
(pp+2)k⋅(1p+2)n−k(\frac{p}{p+2})^k·(\frac{1}{p+2})^{n-k}(p+2p)k⋅(p+21)n−k
每种可能其实相当于在nnn个位置中,选kkk个为止为平,选iii个位置为胜,剩下自然就是负
C(n,k)∗C(n−k,i)C(n,k)*C(n-k,i)C(n,k)∗C(n−k,i)
这里一旦枚举iii就又变成n2n^2n2了
考虑iii的限制,胜场大于负场,即i>n−k−i⇒i∈(n−k2,n−k]i>n-k-i\Rightarrow i\in(\frac{n-k}{2},n-k]i>n−k−i⇒i∈(2n−k,n−k]
随着kkk的枚举,计算的iii范围也要变化,C(n−k,i)C(n-k,i)C(n−k,i)似乎不能通过求和然后乘以一个分数转移到下一个kkk,也就省不掉
考试时,在这里我陷入了僵局
思索许久无果后,我想起了与组合系数紧密相连的杨辉三角
iii的范围对应在上面是一段后缀区间,斜着的不对齐,不能直观冲击我的数学左脑
于是乎,我果断选择枚举负场次数i
这样在杨辉三角里面就是一段左对齐的区间选址
/ | 0 | 1 | 2 | 3 | 4 | 5 | 6 | |
---|---|---|---|---|---|---|---|---|
0 | 1 | 202^020 | ||||||
1 | 1 | 1 | 212^121 | |||||
2 | 1 | 2 | 1 | 222^222 | ||||
3 | 1 | 3 | 3 | 1 | 232^323 | |||
4 | 1 | 4 | 6 | 4 | 1 | 242^424 | ||
5 | 1 | 5 | 10 | 10 | 5 | 1 | 252^525 | |
6 | 1 | 6 | 15 | 20 | 15 | 6 | 1 | 262^626 |
杨辉三角有太多的性质了
- 第iii行的和为2i2^i2i
- 每一行是对称的
这两个性质便是去掉一个nnn复杂度的关键
因为负场是不到一半的
e.g.
胜负场666,负场可能为0/1/20/1/20/1/2,在杨辉三角中就是减去1+6+151+6+151+6+15
这等于先减去中间的C(i,i2)C(i,\frac{i}{2})C(i,2i),再除以二
e.g.
胜负场555,负场可能为0/1/20/1/20/1/2,直接减去Z总和的一半
所以说负场iii的贡献可以根据n−kn-kn−k的奇偶直接算
这中间设计逆元,幂等,需要预处理后O(1)O(1)O(1)调用
卡的就是懒人非要在计算时反复算快速幂的log\loglog
code
#include <cstdio>
#define mod 998244353
#define int long long
#define maxn 3000005
int n, p;
int fac[maxn], inv[maxn], mi_2[maxn], Inv[maxn];int qkpow( int x, int y ) {int ans = 1;while( y ) {if( y & 1 ) ans = ans * x % mod;x = x * x % mod;y >>= 1;}return ans;
}void init() {fac[0] = inv[0] = 1;for( int i = 1;i <= n;i ++ )fac[i] = fac[i - 1] * i % mod;inv[n] = qkpow( fac[n], mod - 2 );for( int i = n - 1;i;i -- )inv[i] = inv[i + 1] * ( i + 1 ) % mod;mi_2[0] = Inv[0] = 1; int mi = 1;for( int i = 1;i <= n;i ++ ) {mi_2[i] = mi_2[i - 1] * 2 % mod;mi = mi * ( p + 2 ) % mod;}Inv[n] = qkpow( mi, mod - 2 );for( int i = n - 1;i;i -- )Inv[i] = Inv[i + 1] * ( p + 2 ) % mod;
}int C( int n, int m ) {return fac[n] * inv[m] % mod * inv[n - m] % mod;
}signed main() {freopen( "fight.in", "r", stdin );freopen( "fight.out", "w", stdout );scanf( "%lld %lld", &n, &p );init();int ans = 0, Inv2 = qkpow( 2, mod - 2 ), mi_p = 1;for( int i = 0;i < n;i ++ ) { //平i场 int t = mi_p * Inv[i] % mod * Inv[n - i] % mod * C( n, i ) % mod;int k; //枚举负场 if( ( n - i ) & 1 ) //胜场+负场=奇数 n-ik = mi_2[n - i - 1];elsek = ( mi_2[n - i] - C( n - i, ( n - i ) >> 1 ) + mod ) % mod * Inv2 % mod;ans = ( ans + k * t % mod * ( i + 1 ) ) % mod;mi_p = mi_p * p % mod;}printf( "%lld\n", ans );return 0;
}