题目链接:点击这里
题目大意:
给定一个长度为
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]=cnt∑dp[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;
}