2019牛客暑期多校训练营(第一场) E ABAB (组合数)

链接:https://ac.nowcoder.com/acm/contest/881/E
来源:牛客网
 

题目描述

Bobo has a string of length 2(n + m) which consists of characters `A` and `B`. The string also has a fascinating property: it can be decomposed into (n + m) subsequences of length 2, and among the (n + m) subsequences n of them are `AB` while other m of them are `BA`.

Given n and m, find the number of possible strings modulo (109+7)(109+7).

输入描述:

The input consists of several test cases and is terminated by end-of-file.

Each test case contains two integers n and m.

* 0≤n,m≤1030≤n,m≤103
* There are at most 2019 test cases, and at most 20 of them has max{n,m}>50max{n,m}>50.

输出描述:

For each test case, print an integer which denotes the result.

示例1

输入

复制

1 2
1000 1000
0 0

输出

复制

13
436240410
1

 

题目大意:有n+m个A和n+m个B求有多少个字符串满足子序列可以分解为n个AB和m个BA。

解题思路:首先可以想到答案就是所有的排列减去不合法的排列。

ans=C(2*(n+m),n+m)-不合法的。

问题的关键在于如何求不合法的字符串的数量。

对于一个字符串我们肯定贪心的让前边的A和B组成AB 让后边的A结合前边的B。

对于一个字符串如果把A的值设为1,B的值设为-1做前缀和

例如:

AABBBBAA

1 2 1 0 -1 -2 -1 0

结合问题发现   前缀和满足[-m,n];

即X为A的个数 Y为B的个数     X-Y属于[-m,n];

满足这个要求的都是合法的字符串。

那么不合法的字符串有多少个呢?

把不和发的字符串分为两类

第一类一前缀和出现X-Y<-m的

第二类是前缀和出现X-Y>n的。

下面的推导过程和卡特兰数的推导过程几乎一致。

设第一次出现前缀和为 -(m+1)的位置为i(不管后边怎么组合,这个字符串已经是不合法的了),则我们把前i个A变成

B,B变成A。

则当前字符串含有n+m+m+1个A,n+m-(m+1)个B。

这个字符串必会出现前缀和为m+1的情况假设位置为j。

我们将前j个字符取反,就得到了原字符串。

所以我们就建立了新的字符串与旧的字符串的一一对应关系,并且新的字符串的个数是好算的。

对于这样的字符串有多少个呢?

C(2*(n+m),n-1)个。所以满足X-Y<-m的字符串的个数有这么多个。

仿照这个思路可以可以求出第二类的字符串有多少个。

上述的关键在于将一类不好求的问题进行映射,得到一个好求的。

以上思路参考:博客

代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long

const int N = 4005;
const int mod = 1e9+7;
LL c[N][N];

void init()
{
    c[0][0]=1;
    for(int i=1;i<=4000;i++)
    {
        c[i][0]=1;
        for(int j=1;j<=i;j++)
        {
            c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
        }
    }
}

LL C(int x,int y)
{
    return c[x][y];
}

int main()
{
    int n,m;
    init();
    while(cin>>n>>m)
    {
        LL ans=C(2*(n+m),n+m);
        if(n)ans=(ans-C(2*(n+m),n-1)+mod)%mod;
        if(m)ans=(ans-C(2*(n+m),m-1)+mod)%mod;
        printf("%lld\n",(ans+mod)%mod);
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值