Description:
n<=5000
题解:
考虑对一个值i它的区间的长度是多少。
区间可以只考虑右半部分的,左边的倒过来做一遍就行了。
若i是固定的,找到右边第一个固定的j>i
那么区间肯定不能超过j所在的位置。
可以直接枚举区间的右端点,这个右端点选的大于i,它左边的小于i,剩余的乱排,用排列数算即可。
这一部分O(n2)O(n^2)O(n2)
若i不是固定的,找到整个序列固定的大于i的,它们把整个序列分成若干段,对于每一段分开做。
考虑枚举相邻j,k(j<k)的为固定点,它们这一段要被统计,则(i,k]里的不固定点都要小于i,那么维护
s[x]=s[x−1]∗P(L,x)∗(S−x−1)!s[x]=s[x-1]*P(L,x)*(S-x-1)!s[x]=s[x−1]∗P(L,x)∗(S−x−1)!
L表示小于i的不定的个数 ,S表示总不定的个数,这样就对一组(j,k)就可以快速查询了。
Code:
#include<cstdio>
#include<algorithm>
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define fd(i, x, y) for(int i = x; i >= y; i --)
using namespace std;
const int mo = 998244353;
ll ksm(ll x, ll y) {
ll s = 1;
for(; y; y /= 2, x = x * x % mo)
if(y & 1) s = s * x % mo;
return s;
}
const int N = 5005;
ll fac[N], nf[N];
int n, a[N];
ll ans, s[N];
int S, L, bz[N], d[N], d0, p[N], p0;
void gg() {
S = 0; fo(i, 1, n) S += !a[i];
fo(i, 1, n) bz[i] = 0;
fo(i, 1, n) bz[a[i]] = i;
L = 0; a[n + 1] = n + 1;
fo(i, 1, n) {
if(bz[i]) {
d0 = 0;
fo(j, bz[i] + 1, n + 1) {
if(a[j] > i) {
d[++ d0] = j; break;
}
if(!a[j]) d[++ d0] = j;
}
ans += fac[S] * (d[1] - bz[i]) % mo;
fo(j, 1, d0 - 1) if(L - j >= 0 && S - j >= 0)
ans += fac[L] * nf[L - j] % mo * fac[S - j] % mo * (d[j + 1] - d[j]) % mo;
ans %= mo;
} else {
s[0] = fac[S - 1];
fo(j, 1, L) s[j] = (s[j - 1] + fac[L] * nf[L - j] % mo * fac[S - j - 1]) % mo;
fo(j, L + 1, n) s[j] = s[L];
a[0] = n + 1; d0 = 0;
fo(j, 0, n + 1) if(a[j] > i) d[++ d0] = j;
fo(j, 1, d0 - 1) {
p0 = 0;
fo(k, d[j], d[j + 1]) if(!a[k]) p[++ p0] = k;
p[++ p0] = d[j + 1];
fo(i, 1, p0 - 1) ans += (p[i + 1] - p[i]) * s[i - 1] % mo;
ans %= mo;
}
L ++;
}
}
}
int main() {
freopen("arrange.in", "r", stdin);
freopen("arrange.out", "w", stdout);
scanf("%d", &n);
fac[0] = 1; fo(i, 1, n) fac[i] = fac[i - 1] * i % mo;
nf[n] = ksm(fac[n], mo - 2);
fd(i, n, 1) nf[i - 1] = nf[i] * i % mo;
fo(i, 1, n) scanf("%d", &a[i]);
gg();
fo(i, 1, n / 2) swap(a[i], a[n - i + 1]);
gg();
ans = ((ans - fac[S] * n) % mo + mo) % mo;
printf("%lld", ans * nf[S] % mo);
}