高精+卡特兰数 bzoj3907网格
问题 A: 网格
时间限制: 1 Sec 内存限制: 256 MB
题目描述
【问题描述】
某城市的街道呈网格状,左下角坐标为A(0, 0),右上角坐标为B(n, m),其中n >= m。现在从A(0, 0)点出发,只能沿着街道向正右方或者正上方行走,且不能经过图示中直线左上方的点,即任何途径的点(x, y)都要满足x >= y,请问在这些前提下,到达B(n, m)有多少种走法。
【输入格式】
输入文件中仅有一行,包含两个整数n和m,表示城市街区的规模。
【输出格式】
输出文件中仅有一个整数和一个换行/回车符,表示不同的方案总数。
【输入样例1】
6 6
【输出样例1】
132
【输入样例2】
5 3
【输出样例2】
28
【数据范围】
50%的数据中,n = m,
在另外的50%数据中,有30%的数据:1 <= m < n <= 100
100%的数据中,1 <= m <= n <= 5 000
结论就是C(n+m,n)-C(n+m,n+1)。
貌似是卡特兰数的基本证明。。。请去度娘找证明,我不会。→_→
剩下的只要搞一个高精即可。
有大佬重载了高精除。。。我。。因为一定是整数,所以我把分子分母强行约分,然后只做乘法,貌似也行。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<vector>
#define N 50005
#define inf 10000
#define ll long long
using namespace std;
int read()
{
int sum=0,f=1;char x=getchar();
while(x<'0'||x>'9'){if(x=='-')f=-1;x=getchar();}
while(x>='0'&&x<='9'){sum=(sum<<1)+(sum<<3)+x-'0';x=getchar();}
return sum*f;
}
int n,m;
int gcd(int x,int y){return !y?x:gcd(y,x%y);}
struct node
{
int f[10005],s;
node(){s=0;memset(f,0,sizeof(f));}
friend node const operator+(node a,node b)
{
if(b.s>a.s)swap(a,b);
int i;
for(i=1;i<=a.s;i++)
{
a.f[i]+=b.f[i];
if(a.f[i]>=inf)
{
a.f[i]-=inf;
a.f[i+1]++;
}
}
if(a.f[a.s+1]>0)a.s++;
return a;
}
friend node const operator-(node a,node b)
{
for(int i=1;i<=a.s;i++)
{
a.f[i]-=b.f[i];
if(a.f[i]<0)
{
a.f[i]+=inf;
a.f[i+1]--;
}
}
while(a.f[a.s]==0)a.s--;
return a;
}
friend node const operator*(node a,int b)
{
int x=0,i;
for(i=1;i<=a.s;i++)
{
a.f[i]*=b;a.f[i]+=x;
x=a.f[i]/inf;
a.f[i]%=inf;
}
if(x!=0)
{
a.s=i;a.f[i]+=x;
}
return a;
}
};
int vis[10005],v[10005];
void hh(node x)
{
printf("%d",x.f[x.s]);
for(int i=x.s-1;i>0;i--)
printf("%04d",x.f[i]);
printf("\n");
}
void work()
{
node sum,cnt;
sum.s=1;sum.f[1]=1;
cnt.s=1;cnt.f[1]=1;
for(int i=2;i<=n;i++)vis[i]=i;
for(int i=m+1;i<=m+n;i++)
{
v[i]=i;
for(int j=2;j<=n;j++)
if(vis[j]!=1)
{
int k=gcd(v[i],vis[j]);if(k==1)continue;
v[i]/=k;vis[j]/=k;
if(v[i]==1)break;
}
}
for(int i=m+1;i<=m+n;i++)if(v[i]!=1)sum=sum*v[i];
for(int i=2;i<=n+1;i++)vis[i]=i;
for(int i=m;i<=m+n;i++)
{
v[i]=i;
for(int j=2;j<=n+1;j++)
if(vis[j]!=1)
{
int k=gcd(v[i],vis[j]);if(k==1)continue;
v[i]/=k;vis[j]/=k;
if(v[i]==1)break;
}
}
for(int i=m;i<=m+n;i++)if(v[i]!=1)cnt=cnt*v[i];
cnt=sum-cnt;
hh(cnt);
}
int main()
{
n=read();m=read();
work();
}