容易发现加入的数是一个排列,但是考虑最后的情况不好做。我们反过来看,枚举最终集合 s s s,判断它是否合法。
那么操作就要反过来:第一个操作从加入任意一个数变成删去任意一个数,第二次操作从删去最大的数变成加入一个最大的数。当然顺序也要反过来。
把当前未出现的数看作空位,假设当前是操作二,如果没有空位就不合法。显然对于第一个操作一定是删去最大的数最优(说明若当前集合有加入的数,一定要把它们先删去,再删原本有的数)。
于是我们现在把操作正着看,发现每个操作二都可以与前面的一个操作一抵消,剩下的操作一就是答案集合,而且它们随时间加入单调递增。
现在我们就可以用 DP 求出最终集合 s s s 的方案数了。设 f i , j f_{i,j} fi,j 表示 s s s 中第 i i i 位填的是 [ 1 , j ] [1,j] [1,j] 范围的数的答案。因为还要保证合法,所以再设 a i a_i ai 表示 s s s 中第 i i i 位最大能填多少。显然 a i a_i ai 是好求的。
下面转移
f
i
,
j
f_{i,j}
fi,j。如果第
i
i
i 位不填,方案是
f
i
,
j
−
1
f_{i,j-1}
fi,j−1;如果第
i
i
i 位填
j
j
j,方案是
f
i
−
1
,
j
−
1
f_{i-1,j-1}
fi−1,j−1(注意
j
≤
a
i
j\le a_i
j≤ai)。于是得出转移:
f
i
,
j
=
f
i
,
j
−
1
+
f
i
−
1
,
j
−
1
f_{i,j}=f_{i,j-1}+f_{i-1,j-1}
fi,j=fi,j−1+fi−1,j−1
于是就做完了。时间复杂度 O ( a b ) O(ab) O(ab)。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod=998244353;
int n,m,a[10001],p[10001],vis[12],cnt,cnt1;
ll f[5001][5001];
int main()
{
cin>>n>>m;
for(int i=1;i<=n+m;i++){
cin>>a[++cnt];
if(a[cnt]==1){
a[cnt]=++cnt1;
}
else{
cnt-=2;
}
}
for(int i=0;i<=n;i++) f[0][i]=1;
for(int i=1;i<=n-m;i++){
for(int j=1;j<=a[i];j++){
f[i][j]=(f[i][j-1]+f[i-1][j-1])%mod;
}
for(int j=a[i]+1;j<=n;j++) f[i][j]=f[i][a[i]];
}
cout<<f[n-m][a[n-m]];
}