Wannafly挑战赛9: D. 造一造(组合数)

链接:https://www.nowcoder.com/acm/contest/71/D
来源:牛客网

题目描述

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

输入描述:

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

输出描述:

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


其实题目就是让你求一个01序列满足:

①总共n个1,n个0(相当于进栈和出栈);

②对于所有前缀1的个数一定要大于等于0的个数;

③额外的限制条件:在某个前缀一定满足有刚好m-1个1和m-k个数字0


如果只有前两个条件,答案刚好就是第n项卡特兰数

其实看第②③个条件,可以得出

对于所有后缀0的个数一定要大于等于1的个数;

⑤在某个后缀一定满足有刚好n-(m-k)个数字0,n-m个数字1


而根据卡特兰数的证明or范德蒙德恒等式,可以得出一个公式:

如果某个前缀一定要满足刚好有a个1和b个0,并且所有的前缀1的个数一定要大于等于0的个数,情况总数为



所以这题答案就是(条件②③)*(条件④⑤)

#include<stdio.h>
#define LL long long
#define mod 1000000007
LL Pow(LL a, LL b);
LL C(LL m, LL n);
LL Lucas(LL m, LL n);
LL Fan(LL n, LL m);
LL inv[2000005] = {1}, jc[2000005] = {1};
int main(void)
{
	LL T, m, n, k, i;
	for(i=1;i<=2000001;i++)
		jc[i] = (jc[i-1]*i)%mod;
	inv[2000001] = Pow(jc[2000001], mod-2);
	for(i=2000000;i>=1;i--)
		inv[i] = inv[i+1]*(i+1)%mod;
	scanf("%d", &T);
	while(T--)
	{
		scanf("%lld%lld%lld", &n, &m, &k);
		printf("%lld\n", Fan(m-1, m-k)*Fan(n-(m-k), n-m)%mod);
	}
	return 0;
}
LL Fan(LL n, LL m)
{
	LL all;
	all = n+m;
	return (C(all, n)-C(all, n+1)+mod)%mod;
}
LL Pow(LL a, LL b)
{
	LL ans;
	ans = 1;
	while(b)
	{
		if(b%2==1)
			ans = (ans*a)%mod;
		a = (a*a)%mod;
		b /= 2;
	}
	return ans;
}
LL C(LL n, LL m)    
{
	LL ans;
	if(n<m)
		return 0;
	ans = jc[n]*inv[m]%mod*inv[n-m]%mod;
	return ans;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值