云空间搭建网站/seo线下培训班
LINK
可以定义f[i][j]f[i][j]f[i][j]表示第一个人上一次选的是pip_ipi,第二个人上一次选的是pjp_jpj,接下来游戏结束的期望值
Ⅰ.当pi<pjp_i<p_jpi<pj时,显然此时应该是第一个人在选数
f[i][j]=1+1c∑k>i&&pk>pjf[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]
Ⅱ.当pi>pjp_i>p_jpi>pj时,显然此时应该是第二个人在选数
f[i][j]=1+1c∑k>j&&pk>pif[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]
考虑枚举外层iii,如何在O(n)O(n)O(n)的时间内计算f[i][1...n]f[i][1...n]f[i][1...n]
对于转移Ⅱ,由于我们是倒序枚举jjj的所以k>jk>jk>j这个条件可以忽略
而合法的kkk还需要满足pk>pip_k>p_ipk>pi而pip_ipi已知
那么倒序枚举jjj的过程中可以一直对符合条件的kkk做f[i][k]f[i][k]f[i][k]的后缀和即可维护
对于转移Ⅰ,由于我们是倒序枚举iii的所以k>ik>ik>i这个条件可以忽略
而pk>pjp_k>p_jpk>pj这个条件,我们对j∈[1,n]j\in[1,n]j∈[1,n]都维护一个sumjsum_jsumj表示目前k∈[i+1.n]k\in[i+1.n]k∈[i+1.n]中满足pk>pjp_k>p_jpk>pj的∑f[k][j]\sum f[k][j]∑f[k][j]
这个也可以动态维护
至此转移复杂度通过后缀和的形式优化到O(n2)O(n^2)O(n2),究其原因还是因为状态与状态之间的合法kkk存在包含关系
是一个非常特殊的转移
#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;
}