您的位置:首页 > 其它

[BZOJ 3907] [JZOJ 3431] 网格

2016-06-11 19:18 295 查看

Description

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



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

Analysis

因为我是蒟蒻,所以这道题我是暴力找规律,找着找着突然发现好像是两个组合数相减。。。

但是比赛的时候没有打完。

好吧,其实可以推出来。

将直线向上平移一格, 则限制条件等价于不能碰到直线。

那么我们将终点按这条直线对称过去,找到一个对称点。

我们发现把起点走到对称点的路径再对称回来,路径即是一条不合法的路径。

所以方案数可以由无限制乱走的方案数-起点走到对称点的方案数。

推一推,就发现方案数就是组合数。(因为dp是杨辉三角)

最后答案为Cmn+m−Cm−1n+m

但是看一下数据范围,并且题目没有要求取余数,所以我们要打高精度了。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=5010;
int n,m,a[N*2];
struct bignum
{
int n,a
;
bignum(){}
bignum(int jy)
{
memset(a,0,sizeof(a));
a[n=1]=jy;
}
}c,e;
void pre(int n,int z)
{
int x=n;
for(int i=2;i*i<=n;i++)
while(!(x%i))
{
x/=i;
a[i]+=z;
}
if(x>1) a[x]+=z;
}
void mul(bignum &c,bignum a,bignum b)
{
c=bignum(0);
c.n=a.n+b.n-1;
fo(i,1,a.n)
fo(j,1,b.n)
{
c.a[i+j-1]+=a.a[i]*b.a[j];
c.a[i+j]+=c.a[i+j-1]/10;
c.a[i+j-1]%=10;
}
while(c.a[c.n+1])
{
c.n++;
c.a[c.n+1]=c.a[c.n]/10;
c.a[c.n]%=10;
}
}
void qmi(bignum &a,int x,int n)
{
e=bignum(x);
for(;n;n>>=1)
{
if(n&1) mul(a,a,e);
mul(e,e,e);
}
}
int main()
{
//the answer is c[n+m][m]-c[n+m][m-1]
scanf("%d %d",&n,&m);
pre(n-m+1,1);
fo(i,m+1,n+m) pre(i,1);
fo(i,2,n+1) pre(i,-1);
c=bignum(1);
fo(i,2,10000)
if(a[i]) qmi(c,i,a[i]);
fd(i,c.n,1) printf("%d",c.a[i]);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: