I - Increasing Subsequence
思路:
直接往期望上面靠。设
为前次取第
个数字,前前次取第
个数字的期望轮数。因为要满足后面的比前面的下标要大,如果从前往后取的话,每次都要找后面的数字中比当前两个数字都大的数字,时间复杂度为
,是接受不了的。既然要求每次都要取后面的数字,那么不如从后面开始转移,转移到前面,就不用再往后找了。
为了保证正确的转移,我们不能将的两维都直接从数组里面取,不然两个都一直变大变小,不太好符合题目要求。尝试固定其中一维为具体的数字,从大到小进行遍历,另一维就能在数组里遍历了。
设为前次取的数字大小为
,前前次取的数字大小为
,取到此状态一直到结束时的期望轮数。设
为第一维是
时的期望和,
为当前比
大的数字的个数。如果当前的
比取的数字
要大的话,
就是一个合法的情况,把此状态下的期望轮数加入期望和
中,并让
;如果
比
要小的话,因为第一维为
,并且第二维数字比
大,下标比
大的情况已经都更新过了,所以就可以用当前的
来更新
。
#include <bits/stdc++.h>
using namespace std;
inline int rd() {
int f = 0; int x = 0; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) f |= (ch == '-');
for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
if (f) x = -x;
return x;
}
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 998244353;
const int N = 5e3 + 7;
ll powmod(ll a, ll b) {
a %= mod;
ll res = 1;
for (; b; b >>= 1) {
if (b & 1) res = res * a % mod;
a = a * a % mod;
}
return res;
}
void add(ll &a, ll b) {
a += b;
a %= mod;
}
ll dp[N][N], inv[N], a[N];
void solve() {
int n = rd();
for (int i = 1; i <= n; ++i) {
a[i] = rd();
}
for (int i = n; i >= 0; --i) {
int cnt = 0;
ll sum = 0;
for (int j = n; j >= 0; --j) {
if (a[j] < i) {
add(dp[a[j]][i], 1 + sum * inv[cnt]);
}
if (a[j] > i) {
add(sum, dp[i][a[j]]);
cnt++;
}
}
}
ll ans = 0;
for (int i = 1; i <= n; ++i) {
add(ans, dp[0][i]);
}
ans = ans * inv[n] % mod;
printf("%lld\n", ans);
}
int main() {
for (int i = 1; i < N; ++i) {
inv[i] = powmod(i, mod - 2);
}
int t = 1;
while (t--) solve();
return 0;
} 
京公网安备 11010502036488号