LINK

可以定义 f [ i ] [ j ] f[i][j] f[i][j]表示第一个人上一次选的是 p i p_i pi,第二个人上一次选的是 p j p_j pj,接下来游戏结束的期望值

Ⅰ.当 p i < p j p_i<p_j pi<pj时,显然此时应该是第一个人在选数

f [ i ] [ j ] = 1 + 1 c ∑ k > i & & p k > p j f [ k ] [ j ] f[i][j]=1+\frac{1}{c}\sum\limits_{k>i\&\&p_k>p_j}f[k][j] f[i][j]=1+c1k>i&&pk>pjf[k][j]

Ⅱ.当 p i > p j p_i>p_j pi>pj时,显然此时应该是第二个人在选数

f [ i ] [ j ] = 1 + 1 c ∑ k > j & & p k > p i f [ i ] [ k ] f[i][j]=1+\frac{1}{c}\sum\limits_{k>j\&\&p_k>p_i}f[i][k] f[i][j]=1+c1k>j&&pk>pif[i][k]

考虑枚举外层 i i i,如何在 O ( n ) O(n) O(n)的时间内计算 f [ i ] [ 1... n ] f[i][1...n] f[i][1...n]

对于转移Ⅱ,由于我们是倒序枚举 j j j的所以 k > j k>j k>j这个条件可以忽略

而合法的 k k k还需要满足 p k > p i p_k>p_i pk>pi p i p_i pi已知

那么倒序枚举 j j j的过程中可以一直对符合条件的 k k k f [ i ] [ k ] f[i][k] f[i][k]的后缀和即可维护

对于转移Ⅰ,由于我们是倒序枚举 i i i的所以 k > i k>i k>i这个条件可以忽略

p k > p j p_k>p_j pk>pj这个条件,我们对 j ∈ [ 1 , n ] j\in[1,n] j[1,n]都维护一个 s u m j sum_j sumj表示目前 k ∈ [ i + 1. n ] k\in[i+1.n] k[i+1.n]中满足 p k > p j p_k>p_j pk>pj ∑ f [ k ] [ j ] \sum f[k][j] f[k][j]

这个也可以动态维护

至此转移复杂度通过后缀和的形式优化到 O ( n 2 ) O(n^2) O(n2),究其原因还是因为状态与状态之间的合法 k k k存在包含关系

是一个非常特殊的转移

#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e3+10;
const int mod = 998244353;
#define int long long
int n,p[maxn],sum[maxn],cnt[maxn],inv[maxn],f[maxn][maxn];
int quick(int x,int n)
{
	int ans = 1;
	for( ; n ; n>>=1,x=x*x%mod )
		if( n&1 )	ans = ans*x%mod;
	return ans;
}
signed main()
{
	for(int i=1;i<=5000;i++)	inv[i] = quick( i,mod-2 );
	cin >> n;
	for(int i=1;i<=n;i++)	cin >> p[i];
	for(int i=n;i>=1;i--)//第一个人上一次的选择
	{
		int knum = 0, sumf = 0;
		for(int j=n;j>=0;j--)//第二个人上一次的选择
		{
			if( i==j )	continue;
			if( p[i]<p[j] )//第一个人开始选数 
			{
				f[i][j] = ( 1+inv[cnt[j]]*sum[j]%mod )%mod;
				knum++; sumf = ( sumf+f[i][j] )%mod;
			}	
			else
			{
				f[i][j] = ( 1+inv[knum]*sumf%mod )%mod;
				cnt[j]++; sum[j] = ( sum[j]+f[i][j] )%mod;
			}
		}	
	}
	int ans = 0;
	for(int i=1;i<=n;i++)	ans = ( ans+f[i][0]*inv[n]%mod )%mod;
	cout << ans; 
}