牛客周赛 Round 31 F.小红的连续段【隔板法+组合数学】

原题链接:https://ac.nowcoder.com/acm/contest/74362/F

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

小红定义一个字符串的“连续段”数量为:相同字符的极长连续子串的数量。例如,"aabbaaa"共有 3 个连续段:"aa"+"bb"+"aaa"。
现在,小红希望你求出,长度为x+y,包含恰好有x个'a'和y个'b'组成的字符串,连续段数量恰好为i的字符串数量。你需要回答i∈[1,x+y]的每个i的答案。

输入描述:

两个正整数x,y,用空格隔开。
1≤x,y≤1000

输出描述:

输出共x+y行,第i行代表连续段数量为i的字符串数量。由于答案可能过大,请对1e9+7取模。

示例1

输入

2 1

输出

0
2
1

说明

两个'a'和一个'b'组成的字符串有 3 个,其中 2 个的连续段数量为 2:"aab"和"baa",其中一个的连续段数量为 3:"aba"

解题思路:

假设有k段,那么必然是以下俩种情况之一

(1)a,b,a,b,a,b,a,....

a有odd=(k+1)/2段,b有even=k/2段,

也就是在a的x-1个空隙中的odd-1个空隙中插入板子隔开,所以有C(x-1,odd-1)

在b的y-1个空隙中的even-1个空隙中插入板子隔开,所以有C(y-1,even-1)。

综合知有C(x-1,odd-1)*C(y-1,even-1)中情况

(2)b,a,b,a,b,a,b,.....

b有odd=(k+1)/2段,a有even=k/2段,

也就是在b的y-1个空隙中的odd-1个空隙中插入板子隔开,所以有C(y-1,odd-1)

在a的x-1个空隙中的even-1个空隙中插入板子隔开,所以有C(x-1,even-1)。

综合知有C(y-1,odd-1)*C(x-1,even-1)中情况

综合上述俩种情况连续段数量为k的字符串数量为C(x-1,odd-1)*C(y-1,even-1)+C(y-1,odd-1)*C(x-1,even-1)

所以我们只需要预处理组合数,然后枚举每一个k即可。

时间复杂度:预处理阶乘O(1000),枚举时间大概为O((x+y)*log(1000)),最终时间复杂度大概为O(1000+(x+y)*log(1000))。

空间复杂度:只使用了存储阶乘的数组,空间复杂度为O(1000)。

cpp代码如下:

#include 
#include 
#include 
#include 

using namespace std;
typedef long long LL;
const int N=1010,mod=1e9+7;

LL fact[N];

LL qmi(LL x,int k)  //快速幂求逆元
{
    LL res=1;
    while(k){
        if(k&1)res=res*x%mod;
        x=x*x%mod;
        k>>=1;
    }
    return res;
}

LL inv(int x)  //求逆元
{
    return qmi(x,mod-2);
}

LL C(int n,int m)  //求组合数C(n,m)
{
    if(m<0 || n-m<0)return 0;  //遇到负数要提前返回0
    return fact[n]*inv(fact[m])%mod*inv(fact[n-m])%mod;
}
int main()
{
    //预处理阶乘
    fact[0]=1;
    for(int i=1;i<=1000;i++)fact[i]=fact[i-1]*i%mod;
    int x,y;
    cin>>x>>y;
    for(int i=1;i<=x+y;i++)  //枚举每一个i,输出答案
    {
        int odd=(i+1)/2,even=i/2;
        cout<<(C(x-1,odd-1)*C(y-1,even-1)%mod+C(y-1,odd-1)*C(x-1,even-1)%mod)%mod<<'\n';
    }
    return 0;
}

你可能感兴趣的:(数学,算法)