有一个长度为2(n+m)的字符串,如果它能划分成(n+m)个长度为2的子序列,使得这些子序列有n个是AB,m个是BA,那么这个字符串就合法。给出n和m,问有多少种合法的字符串。
先给出答案。
下面是分析。
ans= n+m个A和n+m个B任意组合的个数 - 非法字符串的个数
问题一:求x个A和y个B任意组合的个数
我们有x+y个位置,任意选择x个位置放A(B的位置自然也确定了)
所以x个A和y个B任意组合的个数是
那么 n+m个A和n+m个B任意组合的个数就是
问题二:求非法字符串的个数
ps:以下皆以n=1,m=2举例
给出n=1,m=2的合法序列:
ABABAB
ABABBA
ABBAAB
ABBABA
ABBBAA
BAABBA
BAABAB
BABAAB
BABABA
BABBAA
BBAAAB
BBAABA
BBABAA
合法的字符串有什么特性呢?
我们假设A的值为1,B的值为-1。
这样一个字符串就对应一个前缀和序列。
比如上面给出的最后一个字符串BBABAA的前缀和序列为{-1,-2,-1,-2,-1,0}
合法字符串对应的前缀和序列应满足 -m<=sum[i]<=n(1<=i <=2n+2m)
这样我们就可以把非法的字符串划分成两个不相交的集合
即我们从左往右枚举,如果先出现了sum[i]= -(m+1),我们就把它归到第一类,如果先出现了sum[i]=n+1,我们就把它归到第二类
我们以第一类字符串集为例,看看怎么求它的元素个数
这个字符串集元素的特点就是:1.它有n+m个A,n+m个B。2.存在一个最小的i,使得sum[i]=-(m+1)
实际意义是指B在某一时刻过于多,比如一个非法字符串BBBAAA sum[3]=-3 它先消耗了两个BA中的B,第三个B想消耗AB中的B,但此时这个B是没办法使用的
这个字符串集设为s,它的大小直接求不是很好求,但是我们可以构造一个和它一一对应的字符串集t,这样我们求出t的元素个数,就能知道s的
即有函数关系f(s)=t,g(t)=s
假设我们现在有一个第一类非法字符串,它有n+m个A,n+m个B,并且它肯定存在一个最小的i,使得sum[i]=-(m+1) (ps:这个值说明前i个字符中,B比A多m+1个),我们把这前i个字符翻转(A变成B,B变成A),后面的字符不变,就得到了一个唯一确定的字符串。新得到的字符串有以下特性:它有n+m+(m+1)个A,n+m-(m+1)个B,它肯定存在一个最小的j,使得sum[j]=m+1。易得i=j。
这个翻转过程就是函数关系f,新得到的字符串就是s中的一个元素。
字符串集s的特点就是:1.它有n+m+(m+1)个A,n+m-(m+1)个B。2.它肯定存在一个最小的j,使得sum[j]=m+1
相应的,给出一个s中的元素,从左到右枚举找到这个j,我们也能翻转出唯一确定的一个t中的元素。
我们观察s的两个特点,很容易得出,只要满足特点一,就能满足特点二(因为sum[length]=sum[2n+2m]=2(m+1)>=m+1)
也就是说字符串集s的元素个数就是n+m+(m+1)个A,n+m-(m+1)个B任意组合的个数,也就是(见上述问题一证明)
第二类集合的个数相应求出,为
按上述思路再考虑一下n=0或m=0的特殊情况就可以啦
简单说一下:
n>0&&m>0 合法字符串的限制条件是-m<=sum[i]<=n
n>0&&m==0 合法字符串的限制条件是0<=sum[i](其实答案就是卡特兰数,跟括号匹配是一个意思)
n==0&&m>0 合法字符串的限制条件是sum[i]<=0
n==0&&m==0 没有限制条件(答案是1,只有那个空字符串)
#include
#include
using namespace std;
const long long mo=1e9+7;
const int N=4e3+100;
int n,m;
long long jie[N],ni[N],ans;
long long poww(long long a,long long x){
long long ans=1;
while(x){
if(x&1)ans*=a,ans%=mo;
a*=a,a%=mo;
x>>=1;
}
return ans;
}
void init(){
jie[0]=1;
ni[0]=poww(jie[0],mo-2);
for(int i=1;i
推导过程中还有个副产品
设有 a个自由A,a'个受限A,b个自由B,b'个受限B,其中a'<=b,b'<=a(自由和受限的意思:以A为例,自由A的意思是使用的时候没有条件限制;受限A指当我们在放第i个受限A时,前面应至少有i个B(不管是自由B还是受限B))
那么组成的合法字符串个数为
对应的前缀和序列满足-b<=sum[i]<=a
本题是a=b'=n,b=a'=m的情况
dp之后写嘤嘤嘤