BZOJ 3907: 网格 组合数 高精度

3907: 网格

Time Limit: 1 Sec  Memory Limit: 256 MB
Submit: 466  Solved: 215
[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


高精度真是我的梦魇。。不说了。。

不考虑直线到达(n,m)答案为C(n+m,n)

这是加上直线

令n>m (有关系的,看完后面可以画图看一看)

作(n,m)关于y=x+1的对称点

此时新矩形(m-1,n+1)的所有路径都为不符合条件的路径
所以最后答案为
C(n+m,n)-(n+m,n+1)


#include<cmath>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<string>
#include<bitset>
#include<queue>
#include<map>
#include<set>
using namespace std;

inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch<='9'&&ch>='0'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	return x*f;
}
void print(int x)
{if(x<0)putchar('-'),x=-x;if(x>=10)print(x/10);putchar(x%10+'0');}

const int N=10100;

int num[N],num2[N];

inline void cont(int *a,int l,int r,int p)
{
	register int i,j,x;
	for(i=l;i<=r;++i)
	{x=i;for(j=2;(j*j)<=x;++j){while(x%j==0){x/=j;a[j]+=p;}}if(x)a[x]+=p;}
}

struct bignum
{
	int a[N];
	
	friend bignum operator *(const bignum &x,int y)
	{
		bignum b=x;
		register int i;
		for(i=1;i<=b.a[0];i++)b.a[i]*=y;
		for(i=1;i<=b.a[0];i++){if(b.a[i]>=10){b.a[i+1]+=b.a[i]/10;b.a[i]%=10;b.a[0]=max(b.a[0],i+1);}}
		return b;
	}
	
	friend bignum operator -(const bignum &x,const bignum &y)
	{
		bignum b=x;
		register int i;
		for(i=1;i<=y.a[0];i++){b.a[i]-=y.a[i];}
		for(i=1;i<=b.a[0];i++)while(b.a[i]<0){b.a[i]+=10;b.a[i+1]--;}
		while(b.a[0]>1&&!b.a[b.a[0]])b.a[0]--;
		return b;
	}
};

int main()
{
	int n=read(),m=read();
	register int i;
	if(n<m)swap(n,m);
	cont(num,2,n,-1);cont(num,m+1,n+m,1);
	cont(num2,2,n+1,-1);cont(num2,m,n+m,1);
	bignum a,b;
	a.a[0]=b.a[0]=1;a.a[1]=b.a[1]=1;
	for(i=2;i<=N;++i)while(num[i]--)a=a*i;
	for(i=2;i<=N;++i)while(num2[i]--)b=b*i;
	a=a-b;
	for(i=a.a[0];i;i--)print(a.a[i]);puts("");
	return 0;
}
/*
6 6

132
*/

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值