[JZOJ6221] 担心 【区间DP】【概率】

37 篇文章 0 订阅
29 篇文章 0 订阅

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 n100000,A109

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=ji1x=kj1fi,x,kp=xjfx,j,pak+apak
乘上 1 j − i 1\over j-i ji1的原因是只需要保证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=ji1k=ij1fi,ku=k+1jgk,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,mfm,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=ji11k=i+1j1fi,kfk,j(ai+akai+aj+akaj)
它有可能是被区间的两个端点中的任意一个淘汰,一开始乘上 j − i − 1 j-i-1 ji1是由于区间内先塞哪个数都是一样的。

特殊的,我们记 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);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值