2019牛客暑期多校训练营(第一场)E.ABBA 卡特兰数 or dp

大概就是问有多少种序列能够拆出n个AB和m个BA的子序列
E - ABBA
刚开始看到这道题就觉得眼熟,想到卡特兰数了,也确实用到了卡特兰数的证明思想,不知道也没关系

在机械工业出版社的组合数学中,给出这样的问题给出n个+1和n个-1来构成序列,使得序列的任意前缀和为非负数,这样的序列数有多少。

对于非法的序列,我们从头开始找,知道找到第一个前缀和为-1的位置,此时我们把这个前缀翻转,+1变-1,-1变+1,前缀和变成1,这样就有n+1个+1,和n-1个-1,不和合法的序列数就等于用n+1个+1和n-1个-1构成的存在前缀和为+1的数量,因为+1的个数比-1的个数要多,那么无论我们怎么排序,都会出现前缀和==1情况,我们只需要对这前缀和为+1的前k个数进行翻转就是一个非法序列,非法序列的合数就是C(2n,n+1)==C(2n,n),所以合法方案数=总序列数C(2n,n) - 非法序序列数C(2n,n+1),同理对于n个+1,m个-1,n>m的情况,得出

		合法方案数=总序列数C(n+m,n) - 非法序序列数C(n+m,n+1)。

同样的,我们把AB,BA序列中的A看成+1,B看成-1,对于一个n+m个+1和-1构成的合法序列,前缀和始终s满足-m<=s<=n,且前缀和-m<=s<=n,则序列合法,为什么呢

充分性:如果存在前缀和< -m,那么前缀中至少有m+1个B没有配对,这些B不能被AB所使用,则能构成的AB序列小于n个,同理若>n则,能构成的BA序列小于m

即序列合法,则前缀和满足-m<=s<=n;但是前缀和满足-m<=s<=n,不一定是合法序列,我们还得证一下必要性

必要性:若一个序列满足前缀和-m<=s<=n,那么我们可以找到序列中出现的前n个A,我们把第n个A和序列中最后一个B配对取出,第n-1个A和倒数第二个B配对取出,以此类推,这样序列只剩下BB…BBAA…AA了,这样我们可以轻易的取出m个BA

			合法序列 = 总序列 - 非法序列 

那么问题来了,如何求出非法序列呢?
非法序列数=前缀和< -m + 前缀和>n 的方案数,按照n个+1和n个-1计算的思想,我们把前缀和为-1替换成-m-1 和 n+1 就行了。
说到这里可能有人问,< -m 和 >n 的情况会不会同时存在啊。
emmm。。。< -m 和 > n的情况是不会存在交集的,由-m-1到n+1,需要n+1-(-m-1) = n+m+2个A ,A的总数也就n+m个,不会出现这种情况的,所以

		合法序列 = 总序列C(2n+2m,n+m) - 非法序列(C(2n+2m,n-1)+C(2n+2m,m-1))
#include
const int mod = 1e9+7;
using namespace std;

int c[4005][4005];
void init()
{
    int i,j;
    c[0][0] = 1;
    for(i=1;i<4005;i++)
    {
        c[i][0]=c[i][i]=1;
        for(j=1;j0) ans -= c[2*n+2*m][n-1];
        ans = (ans%mod+mod)%mod;
        if(m>0) ans -= c[2*n+2*m][m-1];
        ans = (ans%mod+mod)%mod;
        printf("%d\n",ans);
    }
}

dp的思想其他人都写的比较清楚了,就是枚举前缀长度,通过转移求出-m<=前缀和<=n的方案数,dp[ i ][ j ] = dp[ i-1 ][ j-1 ] + dp[ i-1 ][ j+1 ]
不过题解那句“ 贪心,A 肯定是先用 AB 的 A,再用 BA 的 A;B 同理 ”我是真的没看懂什么意思,可能和我上面的必要性如何取出n个AB的和m个BA的思想一样吧。。。
代码懒得贴了。

你可能感兴趣的:(2019牛客暑期多校训练营(第一场)E.ABBA 卡特兰数 or dp)