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
: 上来直接搞大
D
P
DP
DP,
d
p
i
,
j
:
dp_{i,j}:
dpi,j: 赢了
i
i
i场,输了
j
j
j场的概率,自然就平了
n
−
i
−
j
n-i-j
n−i−j场
枚举每一场是赢了还是输了甚至于平了,这是 O ( n 3 ) O(n^3) O(n3)的,连最基础的测试点都不能拿到
case 1~4
:猜测改写
d
p
i
,
j
:
dp_{i,j}:
dpi,j: 在第
i
i
i轮为止平了
j
j
j的概率,将胜场和负场合并在一起
d p i , j ∗ 1 p + 2 ∗ 2 → d p i + 1 , j dp_{i,j}*\frac{1}{p+2}*2\rightarrow dp_{i+1,j} dpi,j∗p+21∗2→dpi+1,j
d p i , j ∗ p p + 2 → d p i + 1 , j + 1 dp_{i,j}*\frac{p}{p+2}\rightarrow dp_{i+1,j+1} dpi,j∗p+2p→dpi+1,j+1
最后平了 k k k场的方案数,如果胜了 i i i,负了 j j j;则一定对应有一种胜了 j j j,负了 i i i,看似 / 2 /2 /2就行了,总是恰好有一种胜场大于负场
但是如果i=j
,就都不能取,单独减掉
case 5~8
:
n
≤
1
0
5
n\le 10^5
n≤105,没有特殊性质,应该是拿来给选手被卡log
的安慰
case 9~12
:
p
=
1
p=1
p=1,意味着胜负平都是一样的概率,应该可以利用数学方法计算
case 13~20
:最后就是正解了,私以为还是p=1
的数据提供了可以数学计算的灵感
枚举平了 k k k场,因为胜负的概率一样,所以平了 k k k场的每种情况概率都是一样的
( p p + 2 ) k ⋅ ( 1 p + 2 ) n − k (\frac{p}{p+2})^k·(\frac{1}{p+2})^{n-k} (p+2p)k⋅(p+21)n−k
每种可能其实相当于在 n n n个位置中,选 k k k个为止为平,选 i i i个位置为胜,剩下自然就是负
C ( n , k ) ∗ C ( n − k , i ) C(n,k)*C(n-k,i) C(n,k)∗C(n−k,i)
这里一旦枚举 i i i就又变成 n 2 n^2 n2了
考虑 i i i的限制,胜场大于负场,即 i > n − k − i ⇒ i ∈ ( n − k 2 , n − k ] i>n-k-i\Rightarrow i\in(\frac{n-k}{2},n-k] i>n−k−i⇒i∈(2n−k,n−k]
随着 k k k的枚举,计算的 i i i范围也要变化, C ( n − k , i ) C(n-k,i) C(n−k,i)似乎不能通过求和然后乘以一个分数转移到下一个 k k k,也就省不掉
考试时,在这里我陷入了僵局
思索许久无果后,我想起了与组合系数紧密相连的杨辉三角
i i i的范围对应在上面是一段后缀区间,斜着的不对齐,不能直观冲击我的数学左脑
于是乎,我果断选择枚举负场次数i
这样在杨辉三角里面就是一段左对齐的区间选址
/ | 0 | 1 | 2 | 3 | 4 | 5 | 6 | |
---|---|---|---|---|---|---|---|---|
0 | 1 | 2 0 2^0 20 | ||||||
1 | 1 | 1 | 2 1 2^1 21 | |||||
2 | 1 | 2 | 1 | 2 2 2^2 22 | ||||
3 | 1 | 3 | 3 | 1 | 2 3 2^3 23 | |||
4 | 1 | 4 | 6 | 4 | 1 | 2 4 2^4 24 | ||
5 | 1 | 5 | 10 | 10 | 5 | 1 | 2 5 2^5 25 | |
6 | 1 | 6 | 15 | 20 | 15 | 6 | 1 | 2 6 2^6 26 |
杨辉三角有太多的性质了
- 第 i i i行的和为 2 i 2^i 2i
- 每一行是对称的
这两个性质便是去掉一个 n n n复杂度的关键
因为负场是不到一半的
e.g.
胜负场
6
6
6,负场可能为
0
/
1
/
2
0/1/2
0/1/2,在杨辉三角中就是减去
1
+
6
+
15
1+6+15
1+6+15
这等于先减去中间的 C ( i , i 2 ) C(i,\frac{i}{2}) C(i,2i),再除以二
e.g.
胜负场
5
5
5,负场可能为
0
/
1
/
2
0/1/2
0/1/2,直接减去Z总和的一半
所以说负场 i i i的贡献可以根据 n − k n-k n−k的奇偶直接算
这中间设计逆元,幂等,需要预处理后 O ( 1 ) O(1) O(1)调用
卡的就是懒人非要在计算时反复算快速幂的 log \log log
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-i
k = mi_2[n - i - 1];
else
k = ( 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;
}