2021牛客暑期多校训练营1 Increasing Subsequence(概率dp)

题目链接:点击这里

题目大意:
给定一个长度为 n n n 的排列,两个人轮流从这个序列中选择一个数,要求当前回合此人选择的数大于任意一个已经被选择的数,并且该数在数组中的位置 i i i 与此人上一次选择的数在数组中的位置 j j j 要满足 i > j i>j i>j,如果有多个数合法则等概率的从这些数中选一个。当没有合法数时结束,求最终游戏的期望轮数。

题目分析:
d p [ i ] [ j ] dp[i][j] dp[i][j] 为当前玩家上一次选了 i i i ,他的上一个玩家选了 j j j 开始游戏到游戏结束时的数期望个数,则有 d p [ a [ j ] ] [ i ] = ∑ d p [ i ] [ a [ k ] ] c n t + 1 dp[a[j]][i]=\frac{\sum dp[i][a[k]]}{cnt}+1 dp[a[j]][i]=cntdp[i][a[k]]+1 其中 a [ k ] a[k] a[k] 是可以选择的数字, c n t cnt cnt 是可以选择的数的个数
,然后倒着枚举,边转移边维护 c n t cnt cnt,最后把 d p [ 0 ] [ i ] dp[0][i] dp[0][i] 算期望均值即可
具体细节见代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<set>
#include<map>
#include<stack>
#include<queue>
#define ll long long
#define inf 0x3f3f3f3f
//#define int  ll
using namespace std;
int read()
{
	int res = 0,flag = 1;
	char ch = getchar();
	while(ch<'0' || ch>'9')
	{
		if(ch == '-') flag = -1;
		ch = getchar();
	}
	while(ch>='0' && ch<='9')
	{
		res = (res<<3)+(res<<1)+(ch^48);//res*10+ch-'0';
		ch = getchar();
	}
	return res*flag;
}
const int maxn = 5e3+5;
const int mod = 998244353;
const double pi = acos(-1);
const double eps = 1e-8;
ll n,a[maxn],inv[maxn],dp[maxn][maxn];
void init()//线性求逆元
{
	inv[1] = 1;
	for(int i = 2;i < maxn;i++) inv[i] = inv[mod%i]*(mod-mod/i)%mod;
} 
int main()
{
	n = read();init();
	for(int i = 1;i <= n;i++) a[i] = read();
	for(int i = n;i >= 0;i--)
	{
		int sum = 0,cnt = 0;
		for(int j = n;j >= 0;j--)
		{
			if(i > a[j]) dp[a[j]][i] = (sum*inv[cnt]+1)%mod;
			if(i < a[j])
			{
				cnt++;
				sum = (sum+dp[i][a[j]])%mod;
			}
		}
	}
	ll ans = 0;
	for(int i = 1;i <= n;i++) ans = (ans+dp[0][i])%mod;
	cout<<ans*inv[n]%mod<<endl;
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值