CF1919E Counting Prefixes 题解
考虑在 $p$ 中插入一个 $0$。那么题意,即求出满足相邻两项差为 $1$ 且第一个数为 $0$ 的 $p$ 的排列个数。
考虑从小到大插入每一个数。用 $dp_{i,j,x,y}$ 表示插入了前 $i$ 个数,第 $i$ 个数在序列中共有 $j$ 个连通块,开头/结尾的值是否等于这个数($x/y$)的排列个数。因为是从小到大插入,所以当前插入的每一个数的左/右两边的数要么不存在,要么等于当前数 $-1$。
若当前插入的数为 $0$,则从前面转移过来的状态 $x$ 一定等于 $1$,转移后 $x$ 等于 $0$(即不能再在开头插入)。
若当前数不为 $0$,则考虑是否在开头/结尾插入,直接转移即可。
时间复杂度 $O(tn^2)$。
参考代码:
#include<bits/stdc++.h>
#define ll long long
#define mxn 5003
#define md 998244353
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define rept(i,a,b) for(int i=a;i<b;++i)
using namespace std;
int t,n,a[mxn],c[mxn<<1],f[mxn][mxn],dp[2][mxn][2][2];
bool fl;
signed main(){
f[0][0]=1;
rep(i,1,5000){
f[i][0]=1;
rep(j,1,i)f[i][j]=(f[i-1][j-1]+f[i-1][j])%md;
}
scanf("%d",&t);
while(t--){
scanf("%d",&n);
rep(i,0,n<<1)c[i]=0;
c[n]=1,fl=0;
rep(i,1,n){
scanf("%d",&a[i]),c[a[i]+n]++;
if(i>1&&a[i]>a[i-1]+1)fl=1;
}
a[0]=0;
sort(a,a+n+1);
if(fl){
puts("0");
continue;
}
rep(i,1,n+1)dp[0][i][0][0]=dp[0][i][0][1]=dp[0][i][1][0]=dp[0][i][1][1]=0;
if(!a[0]){
dp[0][1][0][1]=1;
}else dp[0][1][1][1]=1;
rep(i,a[0]+n+1,a[n]+n){
fl^=1;
rep(i,1,n+1)dp[fl][i][0][0]=dp[fl][i][0][1]=dp[fl][i][1][0]=dp[fl][i][1][1]=0;
rep(j,1,n+1){
rept(x,0,2)rept(y,0,2)if(dp[fl^1][j][x][y]){
if(i==n){
if(!x)continue;
if(c[i-1]-j+x+y<=c[i])dp[fl][c[i-1]-j+x+y][0][y]=(dp[fl][c[i-1]-j+x+y][0][y]+(ll)dp[fl^1][j][x][y]*f[c[i]-1][c[i-1]-j+x+y-1])%md;
if(y&&c[i-1]-j+x<=c[i])dp[fl][c[i-1]-j+x][0][0]=(dp[fl][c[i-1]-j+x][0][0]+(ll)dp[fl^1][j][x][y]*f[c[i]-1][c[i-1]-j+x-1])%md;
continue;
}
if(c[i-1]-j+x+y&&c[i-1]-j+x+y<=c[i])dp[fl][c[i-1]-j+x+y][x][y]=(dp[fl][c[i-1]-j+x+y][x][y]+(ll)dp[fl^1][j][x][y]*f[c[i]-1][c[i-1]-j+x+y-1])%md;
if(x&&c[i-1]-j+y&&c[i-1]-j+y<=c[i])dp[fl][c[i-1]-j+y][0][y]=(dp[fl][c[i-1]-j+y][0][y]+(ll)dp[fl^1][j][x][y]*f[c[i]-1][c[i-1]-j+y-1])%md;
if(y&&c[i-1]-j+x&&c[i-1]-j+x<=c[i])dp[fl][c[i-1]-j+x][x][0]=(dp[fl][c[i-1]-j+x][x][0]+(ll)dp[fl^1][j][x][y]*f[c[i]-1][c[i-1]-j+x-1])%md;
if(x&&y&&c[i-1]-j&&c[i-1]-j<=c[i])dp[fl][c[i-1]-j][0][0]=(dp[fl][c[i-1]-j][0][0]+(ll)dp[fl^1][j][x][y]*f[c[i]-1][c[i-1]-j-1])%md;
}
}
}
printf("%d\n",(dp[fl][c[a[n]+n]][0][0]+dp[fl][c[a[n]+n]][0][1])%md);
}
return 0;
}