考虑当前还没有被取出来的 \(Y\) 集合中最大的数 \(x\),显然我们只需要保证任意时刻加入堆中的非 \(Y\) 元素大于 \(x\)。
\(x\) 的图像一定是逐渐变小的,我们每一次加入非 \(Y\) 元素时,能选择的范围也在逐步扩大。
经典问题:
我们需要选择一个排列 \(P\),要求 \(1\leq P_i \leq a_i\),其中 \(a_i\) 满足单调不降,求排列的数量。
我们从第一个下标开始决定填什么数,那么显然前面填的数一定在后面的可选范围之中,所以答案就是 \(\prod \limits_{i=1}^n (a_i-i+1)\),也就是每一个数的选择范围减去之前选择了的数量。
那么我们现在的问题就是如何记录 \(Y\) 集合中已经在堆中的元素最大值,由于有删除操作,这个东西变得非常棘手,在 \(M\leq 12\) 的时候可以考虑状压 \(dp\)。
\(M\) 更大的时候我们显然是不能记录这个堆中具体有哪些元素了。我们注意到,每一次都是取出的最小值,如果最大值发生变化了,那么一定满足 最大值已经加入了堆中,且 \(Y\) 集合中所有加入堆的元素已经被取出来。
我们有一个经典思路,对于 \(Y\) 集合中的元素,我们只钦定他们的位置,而不确定他们的具体值,在最大值发生变化的时候再来决定。
具体的讲,我们设 \(f_{i,j,k,l}\) 表示,已经考虑了前 \(i\) 个元素,还没有加入堆中的 \(Y\) 集合元素最大值为 \(a_j\)(\(a\) 就是 \(Y\) 集合中的元素从小到大排序后的数组),堆的大小为 \(k\),\(j\) 是否在堆里面(\(l\))。
每一次转移需要考虑下一个是什么符号,如果是 +
:
- 加入了非 \(Y\) 集合中的元素,根据前面的分析,有 \(f_{i,j,k,l}\times ((n-a_j)-(m-j)-(push_i-pop_i-k))\rightarrow f_{i+1,j,k,l}\)。
\(push_i\) 的意思是在 \(i\) 之前 +
的数量。
\((n-a_j)-(m-j)\) 是比 \(a_j\) 大的非 \(Y\) 集合中的元素个数。
\(push_i-pop_i-k\) 是在前面已经放了多少个非 \(Y\) 集合的元素,就是所有放的元素个数减去已经放了的 \(Y\) 集合中的元素个数。
- 加入了 \(Y\) 集合中的元素
有两种可能,第一种是加入了 \(a_j\),第二种是加入了其他在 \(Y\) 中的数。
在第一种情况下,我们已经确定这个数了,转移系数为 \(1\)。
在第二种情况下,我们已经确定这个数的位置了,但是我们现在还不决定他是什么数,转移系数为 \(1\)。
如果下一个符号是 -
。
考虑什么时候最大值会变?那一定是把最大值从堆中取出来了,此时一定满足 \(k=1,l=1\)。
若 \(k\neq 1\) 或 \(l\neq 1\),有 \(f_{i,j,k,l}\rightarrow f_{i+1,j,k-1,l}\)。
若 \(k=1\) 且 \(l=1\),此时我们枚举下一个最大值是 \(j'\),如果想要最大值变成 \(j’\),那么 \((j',j)\) 中的数必然已经被删除了。此时 \(k=1\),前面的 +
和 -
已经一一匹配,我们在前面添加的数中找 \(j-j'-1\) 个位置放 \((j',j)\),很显然他们的相对位置不会影响最大值的变化,所以再乘上 \((j-j'-1)!\)。
#include<bits/stdc++.h>
using namespace std;
#define N 305
#define p 998244353
#define ll long long
char s[N];
int n,m,a[N<<1],prd[N<<1],pra[N<<1];
ll f[N<<1][N][N][2],fac[N],inv[N],pinv[N];//前 i 个数
inline void upd(ll &x,ll y){(x+=y)%=p;}
inline ll C(int n,int m){return n<m?0:fac[n]*pinv[n-m]%p*pinv[m]%p;}
int main(){
fac[0]=fac[1]=inv[0]=inv[1]=pinv[0]=pinv[1]=1;
for(int i=2;i<N;i++)fac[i]=fac[i-1]*i%p,inv[i]=inv[p%i]*(p-p/i)%p,pinv[i]=pinv[i-1]*inv[i]%p;
scanf("%d%d%s",&n,&m,s+1);
for(int i=1;i<=n+m;i++)prd[i]=prd[i-1]+(s[i]=='-'),pra[i]=pra[i-1]+(s[i]=='+');
for(int i=1;i<=m;i++)scanf("%d",&a[i]);
f[0][m][0][0]=1;
for(int i=0;i<=n+m;i++)for(int j=0;j<=m;j++)for(int k=0;k<=m;k++)for(int l=0;l<2;l++)if(f[i][j][k][l]){
if(s[i+1]=='+'){
upd(f[i+1][j][k+1][l],f[i][j][k][l]);//Y,! j
if(!l)upd(f[i+1][j][k+1][1],f[i][j][k][l]);//Y j
upd(f[i+1][j][k][l],f[i][j][k][l]*(n-a[j]-(m-j)-(pra[i]-prd[i]-k))%p);//!Y
}
else if(k){
if(k>1||l==0)
upd(f[i+1][j][k-1][l],f[i][j][k][l]);
else for(int tj=0;tj<j;tj++)
upd(f[i+1][tj][0][0],f[i][j][k][l]*C(prd[i]-(m-j),j-tj-1)%p*fac[j-tj-1]%p);
}
}
printf("%lld",f[n+m][0][0][0]);
}