Description
有n个人,每个人有水平值 A i A_i Ai
每一轮随机挑选剩余的相邻两人单挑,假设两人能力值为 a , b a,b a,b,则能力值为 a a a的人有 a a + b a\over a+b a+ba的概率获胜,能力值为 b b b的人有 b a + b b\over a+b a+bb的概率获胜。
给出n,m以及A,问第m个人获胜的概率,答案对998244353取模。
n ≤ 100000 , A ≤ 1 0 9 n\leq 100000,A\leq 10^9 n≤100000,A≤109
Solution
一个朴素的 O ( n 5 ) O(n^5) O(n5)的DP是这样的
记 f i , j , k f_{i,j,k} fi,j,k表示当前考虑区间 [ i , j ] [i,j] [i,j],获胜的人为 k k k
考虑有左右两半两个区间转移而来
假定k在左边的区间,在右边的区间情况类似
f
i
,
j
,
k
=
1
j
−
i
∑
x
=
k
j
−
1
f
i
,
x
,
k
∑
p
=
x
j
f
x
,
j
,
p
a
k
a
k
+
a
p
f_{i,j,k}={1\over j-i}\sum\limits_{x=k}^{j-1}f_{i,x,k}\sum\limits_{p=x}^{j}f_{x,j,p}{a_k\over a_k+a_p}
fi,j,k=j−i1x=k∑j−1fi,x,kp=x∑jfx,j,pak+apak
乘上
1
j
−
i
1\over j-i
j−i1的原因是只需要保证k和p单挑的这一局是在这个区间内所有比赛的最后一场。
答案显然是 f 1 , n , m f_{1,n,m} f1,n,m
考虑优化这个DP,记 f i , j f_{i,j} fi,j为考虑区间 [ i , j ] [i,j] [i,j],最终 i i i获胜的概率, g i , j g_{i,j} gi,j为最终 j j j获胜的概率
由于一个位置的左右边的比赛是独立的,因此 [ l , r ] [l,r] [l,r]内 k k k获胜的概率就是 f l , k × g k , r f_{l,k}\times g_{k,r} fl,k×gk,r
容易得到转移
f
i
,
j
=
1
j
−
i
∑
k
=
i
j
−
1
f
i
,
k
∑
u
=
k
+
1
j
g
k
,
u
f
u
,
j
a
i
a
i
+
a
u
f_{i,j}={1\over j-i}\sum\limits_{k=i}^{j-1}f_{i,k}\sum\limits_{u=k+1}^{j}g_{k,u} f_{u,j}{a_i\over a_i+a_u}
fi,j=j−i1k=i∑j−1fi,ku=k+1∑jgk,ufu,jai+auai
交换主体,我们发现
∑
k
f
i
,
k
g
k
,
u
a
i
a
i
+
a
u
\sum_{k}f_{i,k}g_{k,u}{a_i\over a_i+a_u}
∑kfi,kgk,uai+auai这一部分只与i,u有关,可以在枚举j以外预处理。
这样转移就不需要枚举k,复杂度降低到 O ( n 3 ) O(n^3) O(n3),能够通过本题。
事实上还有另外一种做法。
我们考虑将整个过程倒过来,从序列中淘汰变成向序列中塞数。
记 f i , j f_{i,j} fi,j表示区间 [ i , j ] [i,j] [i,j]中只塞进了 i , j i,j i,j,中间的所有均为空的概率。
最后的答案就是 f 0 , m ∗ f m , n + 1 f_{0,m}*f_{m,n+1} f0,m∗fm,n+1
考虑转移,枚举塞哪一个数进去
f
i
,
j
=
1
j
−
i
−
1
∑
k
=
i
+
1
j
−
1
f
i
,
k
f
k
,
j
(
a
i
a
i
+
a
k
+
a
j
a
j
+
a
k
)
f_{i,j}={1\over j-i-1}\sum\limits_{k=i+1}^{j-1}f_{i,k}f_{k,j}({a_i\over a_i+a_k}+{a_j\over a_j+a_k})
fi,j=j−i−11k=i+1∑j−1fi,kfk,j(ai+akai+aj+akaj)
它有可能是被区间的两个端点中的任意一个淘汰,一开始乘上
j
−
i
−
1
j-i-1
j−i−1是由于区间内先塞哪个数都是一样的。
特殊的,我们记 a 0 = a n + 1 = 0 a_{0}=a_{n+1}=0 a0=an+1=0
边界条件为 f i , i + 1 = 1 f_{i,i+1}=1 fi,i+1=1
这样也在 O ( n 3 ) O(n^3) O(n3)的时间复杂度内解决了问题,后一种方法理解难度较大,但代码难度相当低。
Code
#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
const int N=505;
const int mo=998244353;
typedef long long LL;
using namespace std;
int n,m,a[N];
LL f[N][N],ns[N][N],ny[N];
LL ksm(LL k,LL n)
{
LL s=1;
for(;n;n>>=1,k=k*k%mo) if(n&1) s=s*k%mo;
return s;
}
#define inc(x,y) (x=(x+y)%mo)
int main()
{
cin>>n>>m;
fo(i,1,n) scanf("%d",&a[i]);
fo(i,0,n) f[i][i+1]=1;
fo(i,1,n)
fo(j,i+1,n) ns[i][j]=ns[j][i]=ksm(a[i]+a[j],mo-2);
fo(i,1,n) ny[i]=ksm(i,mo-2);
fo(l,2,n)
{
fo(x,0,n-l+1)
{
int y=x+l;
fo(p,x+1,y-1)
{
inc(f[x][y],f[x][p]*f[p][y]%mo*((ns[x][p]*a[x]+ns[p][y]*a[y])%mo)%mo);
}
f[x][y]=f[x][y]*ny[l-1]%mo;
}
}
printf("%lld\n",f[0][m]*f[m][n+1]%mo);
}