可以定义 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>pj∑f[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>pi∑f[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;
}