Increasing Subsequence —— 1I
Increasing Subsequence
题目描述
给出排列P,两个人轮流取数,每次取的数需要在之前该人取数的右边,且比当前取出来所有的数都要大。所有当前可选的数都将等概率随机的被当前决策人选中。问两个人期望取数的轮数。
范围
\(n \leq 5000\)
题解
概率dp,设\(f_{i,j}\)表示第一个人上一步选了\(a_i\),第二个人选了\(a_j\),当前的答案。
- \(a_i > a_j\)
\(f_{i,j} = 1 + {1 \over c} \times \sum_{k > j,a_k > a_i} f_{i,k}\)
- \(a_i < a_j\)
\(f_{i,j} = 1 + {1 \over c} \times \sum_{k > i,a_k > a_j} f_{k,j}\)
注意到计算时可以使用前缀和优化,复杂度\(O(n^2)\)
代码
#include
using namespace std;
const int N = 5010;
const int mod = 998244353;
#define ll long long
int pow_mod(int a,int b) {
int res = 1;
while(b) {
if(b & 1) res = 1ll * res * a % mod;
a = 1ll * a * a % mod;
b >>= 1;
}
return res % mod;
}
ll f[N][N];
ll sum[N];
int inv[N];
int a[N];
ll cnt[N];
int main () {
int n;
cin >> n;
for(int i = 1;i <= n; ++i) {
cin >> a[i];
}
for(int i = 1;i <= n; ++i) {
inv[i] = pow_mod(i,mod - 2);
}
for(int i = n;i >= 1; --i) {
ll s = 0;
ll c = 0;
for(int j = n;j >= 0; --j) {
if(i == j) continue;
if(a[i] > a[j]) {
f[i][j] = (1 + s * inv[c]) % mod;
sum[j] = (sum[j] + f[i][j]) % mod;
cnt[j] ++;
}
else {
f[i][j] = (1 + sum[j] * inv[cnt[j]]) % mod;
s = (s + f[i][j]) % mod;
c ++;
}
}
}
ll ans = 0;
for(int i = 1;i <= n; ++i) {
ans = (ans + f[i][0]) % mod;
}
cout << 1ll * ans * inv[n] % mod << endl;
return 0;
}