思路
容斥。
钦定有 个分解线, 段中后面的不能连向前面的,每段中可能还有更小的段,系数为 。
规定一个段以白点结尾。可以合法的前缀中每个黑点都要被前缀中的白点匹配,即前缀中白点数要多于黑点。
把所有白点单独拉出来,设它们在原序列中下标为 ,在这个长为 的新序列上 dp。
设 表示钦定最后一段结尾为 的带系数的方案数。枚举前一个 , 中匹配了 对,剩 个白点,选出 个和 中的黑点匹配。
半在线卷积。,将 相同的 放一起,可以看作 和 卷积。即下标在 和 范围内的两个数组卷积,虽然单个长度没有办法保证,但是一整层的长度之和保证为 。
复杂度 。
code
int n;
char s[maxn<<1];
int p[maxn],f[maxn];
int fac[maxn],inv[maxn];
int C(int m,int n){return fac[m]*inv[n]%mod*inv[m-n]%mod;}
void sovle(int l,int r){
if(l==r){
if(l>=p[l]-l)f[l]=(C(l,p[l]-l)*fac[p[l]-l]+mod-f[l])%mod;
else f[l]=0;
return ;
}
int mid=l+r>>1;
sovle(l,mid);
int fl=p[l]-l,fr=p[mid]-mid,gl=max(0ll,mid+1-(p[mid]-mid)),gr=max(0ll,r-(p[l]-l));
vector<int> ff(fr-fl+1),gg(gr-gl+1);
for(int i=l;i<=mid;i++)if(i>=p[i]-i)(ff[p[i]-i-fl]+=f[i])%=mod;
for(int i=gl;i<=gr;i++)gg[i-gl]=fac[i];
vector<int> hh=mul(ff,gg);
for(int i=mid+1;i<=r;i++)if(i>=p[i]-i)(f[i]+=hh[i-fl-gl]*inv[2*i-p[i]])%=mod;
sovle(mid+1,r);
}
void work(){
n=read();scanf("%s",s+1);
if(s[1]=='W'||s[2*n]=='B'){puts("0");return ;}
n=0;for(int i=1;s[i]=='W'||s[i]=='B';i++)if(s[i]=='W')p[++n]=i;
fac[0]=1;for(int i=1;i<=n;i++)fac[i]=fac[i-1]*i%mod;
inv[n]=ksm(fac[n]);for(int i=n-1;~i;i--)inv[i]=inv[i+1]*(i+1)%mod;
sovle(1,n);
// for(int i=1;i<=n;i++){
// f[i]=C(i,p[i]-i)*fac[p[i]-i]%mod;
// for(int j=1;j<i;j++)(f[i]+=mod-C(i-(p[j]-j),p[i]-i-(p[j]-j))*fac[p[i]-i-(p[j]-j)]%mod*f[j]%mod)%=mod;
// // cout<<i<<" "<<p[i]<<" "<<f[i]<<"\n";
// }
// for(int i=1;i<=n;i++)cout<<i<<" "<<p[i]<<" "<<f[i]<<"\n";
printf("%lld\n",f[n]);
}