Wannafly挑战赛9 造一造||2018年长沙理工大学程序设计竞赛 杯子 【卡特兰数】



题目描述

WYF正试图用一个栈来构造一棵树,现在他已经构造了n个元素作为树的节点,只要将这n个元素依次入栈出栈就可以形成一棵树了。当然,这个问题与树并没有关系,所以它叫做WYF的栈。每次你可以入栈一个新元素或者当栈非空时出栈一个元素,n个元素必须依次入栈,而WYF希望其中第m个元素入栈之后,栈中恰好有k个元素,现在他想知道一共有多少种入栈出栈顺序满足这个条件。

输入描述:

第一行一个正整数T,表示数据组数。(1<=T<=10000)
对于每组数据包含一行三个正整数n,m,k

输出描述:

 对于每组数据输出一个正整数表示答案。

 由于答案可能过大,所以只需要输出对109+7取模后的答案

 

示例1

输入

2

3 3 3

3 3 2

输出

1

2

示例2

输入

5

10 3 2

10 2 2

10 7 5

10 6 2

10 7 6

输出

6864

11934

2200

3780

924

示例3

输入

2

5 4 4

5 2 1

输出

5

14

备注:

1<=n,m,k<=106

 


【题目链接】 link


题目描述

一天durong同学买了一个无限长的杯子,同时买了n个球,并且标号为123......ndurong同学突然想到一个问题----如果他把n个球依次,也就是按照123...n的顺序放进杯子里,然后在全部拿出来(注意不一定要等到全部放进去才能拿出球),并且会记录放进和拿出球的顺序,
durong
想知道,要满足当第m个球进去后,杯子中此时恰好有k个球,然后仍然要把剩下的n-m个球放进去,最后杯中的球要取光,这样的放进和拿出球的顺序有多少种,答案有可能很大,所以mod1e9+7

输入描述:

1<=n,m,k<=1e6m可能大于nk可能大于m
第一行一个正整数T,表示数据组数。(1<=T<=10000)
对于每组数据包含一行三个正整数n,m,k

输出描述:

对于每组数据输出一个正整数表示答案。
由于答案可能过大,所以只需要输出对1e9+7取模后的答案

示例1

输入

2

3 3 3

3 3 2

输出

1

2


【题目链接】 link


【思路】

这两道题目其实是一样的,就是求一个01序列满足:

  1. 一共有n个0,n个1需要进栈和出栈
  2. 对于所有的前缀,0的个数要大于等于1的个数
  3. 此题特殊要求:存在某个前缀满足刚好有m-1个0和m-k个1(因为第m个一定是0)

如果只有条件1,2,显然答案就是第n项卡特兰数。而根据条件2,3,我们可以反推到条件4,5

  1. 对于所有的后缀,1的个数要大于等于0的个数
  2. 存在某个后缀满足刚好有m-(m-k)个1和n-m个0

根据卡特兰数的结论,如果要满足某个前缀满足刚好有n个0和m个1,且所有前缀中0的个数均大于等于1的个数,则情况总数为C(n+m,n)-C(n+m,m-1)

那么情况总数就是2和3的方案数乘以4和5的方案数


#include <cstdio>
#include <bits/stdc++.h>
#include <cmath>
#include <map>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
#define rush() int T;scanf("%d",&T);while(T--)

typedef long long ll;
const int maxn = 2000005;
const ll mod = 1e9+7;
const int INF = 1e9;
const double eps = 1e-6;

int n,m,k;
ll fac[maxn],inv[maxn];

ll fast_mod(ll a,ll n,ll Mod)
{
    ll ans=1;
    while(n)
    {
        if(n&1) ans=(ans*a)%Mod;
        a=(a*a)%Mod;
        n/=2;
    }
    return ans;
}

void init()
{
    fac[0]=1;
    for(int i=1;i<maxn;i++) fac[i]=(fac[i-1]*i)%mod;
    inv[maxn-1]=fast_mod(fac[maxn-1],mod-2,mod);
    for(int i=maxn-2;i>=0;i--) inv[i]=(inv[i+1]*(i+1))%mod;
}

ll C(int n,int m)
{
    if(n<m) return 0;
    return fac[n]*inv[m]%mod*inv[n-m]%mod;
}

ll cal(int n,int m)
{
    return (C(n+m,n)-C(n+m,m-1)+mod)%mod;
}

int main()
{
    init();
    rush()
    {
        scanf("%d%d%d",&n,&m,&k);
        if(m>n||k>m)
        {
            puts("0");
            continue;
        }
        printf("%lld\n",cal(m-1,m-k)*cal(n-(m-k),n-m)%mod);
    }
}






  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值