【GDOI2014模拟】网格 题解+代码

来源:互联网 发布:cad网络机柜 交换机 编辑:程序博客网 时间:2024/05/16 09:47

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

输入1:
6 6
输入2:
5 3

Sample Output

输出1:
132
输出2:
28

Data Constraint

50%的数据中,n = m,在另外的50%数据中,有30%的数据:1 <= m < n <= 100
100%的数据中,1 <= m <= n <= 5 000

Solution

首先可以得出,从(0,0)不加那条限制的线走到(x,y)的方案为Cyx+y
那么将限制的线向上平移一个单位,限制就是不能碰到线。那么加上限制的方案数就是不加限制走到(x,y)-不加限制走到(x,y)关于平移后的线的对称点。因为走到对称点的线必定碰到了这条线。那么方案就是 Cyx+yCy1x+y
那么按照公式做高精度就行了。
不过有一种稍微简单点的方法,就只要打高精度乘或除单精度。
先把公式化简,变成(n+m)!(n+1m)/((n+1)!m!)
先把(n+1m)丢一边去,那么分母就是(n+1)!m!,显然前面那个大一点,那就先和上面约掉变成(n+2)(n+3)(n+m)这里乘数的每一个都是单精度,然后除的时候分别除1,除2,……除m,最后再乘上丢掉的那个东西,就行了。

type arr=array[0..100000] of int64;var    i,j,k,n,m:longint;    mo:int64=1000000;    ans:arr;function min(a,b:longint):longint;begin if a<b then exit(a)else exit(b);end;procedure cheng(var a:arr;b:int64);var    i:longint;    c:arr;begin    fillchar(c,sizeof(c),0);    for i:=1 to a[0] do    begin        a[i]:=a[i]*b+c[i];        if a[i]>mo then        begin            c[i+1]:=c[i+1]+(a[i] div mo);a[i]:=a[i] mod mo;            if c[i+1]>mo then begin c[i+2]:=c[i+2]+(c[i+1] div mo);c[i+1]:=c[i+1]mod mo;end;        end;    end;    while c[a[0]+1]<>0 do begin inc(a[0]);a[a[0]]:=c[a[0]];end;end;procedure chu(var a:arr;b:int64);var    i:longint;    c,d:int64;begin    c:=0;    for i:=a[0] downto 1 do    begin        d:=(a[i]+c)mod b;        a[i]:=(a[i]+c) div b;c:=d*mo;    end;    while (a[a[0]]=0) do dec(a[0]);end;procedure printf(a:arr);var    i,j:longint;    s:string;begin    write(a[a[0]]);    for i:=a[0]-1 downto 1 do    begin        str(a[i],s);        for j:=length(s)+1 to 6 do write(0);        write(a[i]);    end;    writeln;end;begin    read(n,m);ans[0]:=1;ans[1]:=1;    for i:=n+2 to n+m do cheng(ans,i);    cheng(ans,n+1-m);    for i:=2 to m do chu(ans,i);    printf(ans);end.
0 0