是青白呀
2024-11-18 22:13:52
首先一个经典的结论是:一轮冒泡排序做的事,实际上是将序列按照每一个前缀最大值划分成若干个区间,并将每个区间的最大值(初始时在最前面)循环移位至区间末尾。
考虑记录前缀最大值进行 dp。我们预处理
若
若
使用前缀和优化掉第一种情况的枚举
#include<bits/stdc++.h>
#define rep(i,j,k) for(int i=j;i<=k;i++)
#define repp(i,j,k) for(int i=j;i>=k;i--)
#define ls(x) (x<<1)
#define rs(x) ((x<<1)|1)
#define mp make_pair
#define sec second
#define fir first
#define pii pair<int,int>
#define lowbit(i) i&-i
#define double long double
#define int long long
#define qingbai 666
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=5002,M=15,S=(1<<8)+5,inf=(ll)1e18+7,mo=998244353;
const double eps=1e-8;
void read(int &p){
int x=0,w=1;
char ch=0;
while(!isdigit(ch)){
if(ch=='-')w=-1;
ch=getchar();
}
while(isdigit(ch)){
x=(x<<1)+(x<<3)+ch-'0';
ch=getchar();
}
p=x*w;
}
int n,a[N];
int f[N][N],sum[N][N],pren[N],num[N],pos[N];
bool equ(int x,int v){
if(a[x]>0)return a[x]==v;
return !pos[v];
}
signed main(){
read(n);
rep(i,1,n){
read(a[i]);
if(a[i]!=-1)pos[a[i]]=i;
pren[i]=pren[i-1]+(a[i]==-1);
}
if((a[n]>0&&a[n]!=n)||(a[n]==-1&&pos[n])){
puts("0");
return 0;
}
rep(i,1,n)
num[i]=num[i-1]+(!pos[i]);
f[0][0]=1;
rep(i,0,n)
sum[0][i]=1;
rep(i,1,n){
rep(j,1,n){
//1.i is prefix max
if(!pos[j]||pos[j]>=i)f[i][j]+=sum[i-1][j-1];
//2.i isnot
if(a[i-1]!=-1){
if(a[i-1]<j)f[i][j]+=f[i-1][j];
}
else f[i][j]+=f[i-1][j]*(num[j-1]-(pren[i-1]-1));
f[i][j]%=mo;
sum[i][j]=(sum[i][j-1]+f[i][j]*equ(i,j))%mo;
}
}
printf("%lld\n",f[n][n]);
return 0;
}