bzoj 3907: 网格 (卡特兰数+组合数学+高精度)

3907: 网格

Time Limit: 1 Sec   Memory Limit: 256 MB
Submit: 398   Solved: 178
[ Submit][ Status][ Discuss]

Description

某城市的街道呈网格状,左下角坐标为A(0, 0),右上角坐标为B(n, m),其中n >= m。现在从A(0, 0)点出发,只能沿着街道向正右方或者正上方行走,且不能经过图示中直线左上方的点,即任何途径的点(x, y)都要满足x >= y,请问在这些前提下,到达B(n, m)有多少种走法。

Input

输入文件中仅有一行,包含两个整数n和m,表示城市街区的规模。

Output

输出文件中仅有一个整数和一个换行/回车符,表示不同的方案总数。

Sample Input

6 6

Sample Output

132

HINT

100%的数据中,1 <= m <= n <= 5 000


Source

[ Submit][ Status][ Discuss]


题解:卡特兰数+组合数学+高精度

这道题相当于经典的购票问题。

最后的答案是C(n+m,n)-C(n+m,n+1)

这里有一些有关卡特兰数问题的讲解:http://blog.csdn.net/wuyuegb2312/article/details/9339529 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 10003
using namespace std;
struct data {
	int s[N],len;
	data (){
		memset(s,0,sizeof(s));
		len=0;
	}
}ans,ans1;
int n,m,prime[N],mp[N],pd[N],num[N];
void init()
{
	for (int i=2;i<=10000;i++) {
		if (!pd[i]) prime[++prime[0]]=i,mp[i]=prime[0];
		for (int j=1;j<=prime[0];j++) {
			if (prime[j]*i>10000) break;
			pd[prime[j]*i]=1;
			if (i%prime[j]==0) break;
		}
	}
}
void calc(int x,int val)
{
	int k=x;
	for (int i=1;prime[i]*prime[i]<=k;i++) 
	 if (k%prime[i]==0) {
	 	while (k%prime[i]==0) 
	 	 num[i]+=val,k/=prime[i];
	 }
	if (k>1) num[mp[k]]+=val;
}
data mul(data a,int x)
{
	data c; int t=a.len;
	for (int i=1;i<=a.len;i++) 
	 c.s[i]=a.s[i]*x;
	for (int i=1;i<=t;i++) {
		c.s[i+1]+=c.s[i]/10;
		c.s[i]%=10;
	}
	while (c.s[t+1]) {
		t++; c.s[t+1]+=c.s[t]/10;
		c.s[t]%=10;
	}
	c.len=t;
	return c;
}
data jian(data a,data b)
{
	data c;
	int t=a.len;
	for (int i=1;i<=t;i++) c.s[i]=a.s[i]-b.s[i];
	for (int i=1;i<=t;i++) 
	 if (c.s[i]<0) c.s[i]+=10,c.s[i+1]--;
	while (!c.s[t]) t--;
	c.len=t;
	return c; 
}
int main()
{
	freopen("a.in","r",stdin);
	freopen("my.out","w",stdout);
	scanf("%d%d",&n,&m);
	init();
	for (int i=n+1;i<=n+m;i++) calc(i,1);
	for (int i=1;i<=m;i++) calc(i,-1);
	ans.len=ans.s[1]=1;
	for (int i=1;i<=prime[0];i++) 
	 while (num[i]) ans=mul(ans,prime[i]),num[i]--;
	//for (int i=ans.len;i>=1;i--) printf("%d",ans.s[i]);
	//printf("\n");
	for (int i=m;i<=n+m;i++) calc(i,1);
	for (int i=1;i<=n+1;i++) calc(i,-1);
    ans1.len=ans1.s[1]=1;
	for (int i=1;i<=prime[0];i++) 
	 while (num[i]) 
	  ans1=mul(ans1,prime[i]),num[i]--;
	//for (int i=ans1.len;i>=1;i--) printf("%d",ans1.s[i]);
	//printf("\n");
	ans=jian(ans,ans1);
	for (int i=ans.len;i>=1;i--) printf("%d",ans.s[i]);
	printf("\n");
}




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值