题意
A l i c e Alice Alice和 B o b Bob Bob 玩游戏,规定每轮每个人在序列中选一个数字,要求是选择的数字的大小一定是比之前两人选过的数字都要大,选择数字在序列中的下标要比该选手选过的数字下标要大。
题解
我们发现该选手选择序列的限制有两条,我们想到是否可以使用dp来表示限制然后转台转移模拟选择的数字,然后我们开始设置状态, f [ i ] [ j ] f[i][j] f[i][j]表示Alice选手前一次选择了数字 j j j, i i i则表示 B o b Bob Bob选手上一次选择的数字的期望次数 g [ i ] [ j ] g[i][j] g[i][j]同上,只不过是转换了角色,我们发现这样复杂度有点高, ( N 3 ) (N^3) (N3),一般来说我们可以寻求各种优化,但是想了好久发现怎样优化都是不好做,但是我们发现 A l i c e Alice Alice和 B o b Bob Bob是等价的,考虑换一种状态表示, d p [ i ] [ j ] dp[i][j] dp[i][j]表示的是上一个人选择数字 i i i,然后该人选择数字 j j j到达最终状态的期望步数,Q:为什么要是到达最终状态的期望步数呢?这样是为了我们后边对其进行优化,假设我们设置是到达该状态的期望步数,那么我们的状态转移就变成了, d p [ i ] [ j ] = ( d p [ k ] [ i ] + 1 ) / c n t dp[i][j] = (dp[k][i] + 1) / cnt dp[i][j]=(dp[k][i]+1)/cnt,cnt表示的是上一个选手的可选择数量,我们发现这样向后转移是很难优化的,只是计算cnt大小就已经很难。我们于是开始考虑状态从后向前更新,于是状态表示就变成了"到达最终状态的期望步数"。我们开一个数组 p o s pos pos表示对给定序列的映射,我们开始在pos数组上进行dp,考虑状态转移, d p [ i ] [ j ] = ( d p [ j ] [ k ] + 1 ) / c n t dp[i][j] = (dp[j][k] + 1)/ cnt dp[i][j]=(dp[j][k]+1)/cnt,这样我们计算cnt就可以用一个前缀数组来维护。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <ctime>
#include <vector>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <cmath>
using namespace std;
#define x first
#define y second
typedef long long LL;
typedef pair <int, int> PII;
const int N = 5e3 + 10, INF = 0x3f3f3f3f, mod = 998244353;
int dp[N][N], a[N], p[N], unf[N];
int main()
{
clock_t c1 = clock();
ios::sync_with_stdio(false);
cin.tie(0);
#ifdef LOCAL
freopen("in.in", "r", stdin);
freopen("out.out", "w", stdout);
#endif
// ------------------------------------------------------------------------------------------------>
int n; cin >> n;
for (int i = 1; i <= n; i ++) cin >> a[i];
for (int i = 1; i <= n; i ++) p[a[i]] = i;
unf[1] = unf[0] = 1;
for (int i = 2; i <= n; i ++) unf[i] = (LL)(mod - mod / i) * unf[mod % i] % mod;
for (int i = n; i >= 1; i --)
{
vector <int> sum (n + 5, 0), cnt (n + 5, 0);
for (int j = i + 1; j <= n; j ++)
{
cnt[p[j]] ++;
sum[p[j]] = dp[i][j];
}
for (int j = n; j >= 0; j --)
{
cnt[j] += cnt[j + 1];
sum[j] = ((LL)sum[j + 1] + sum[j]) % mod;
}
for (int j = 0; j < i; j ++)
{
if(cnt[p[j]]) dp[j][i] = ((LL)sum[p[j]] * unf[cnt[p[j]]] % mod + 1) % mod;
}
}
LL ans = 0;
for (int i = 1; i <= n; i ++) ans = (ans + dp[0][i]) % mod;
ans = (ans * unf[n] % mod + 1) % mod;
cout << ans << '\n';
// ------------------------------------------------------------------------------------------------>
end:
cerr << "Time Use -----> " << clock() - c1 << "ms" << endl;
return 0;//
}