JZOJ.3431【GDOI2014模拟】网格 解题报告

来源:互联网 发布:java如何进行异常处理 编辑:程序博客网 时间:2024/04/30 03:03

网格

题目描述

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

样例输入

6 3

样例输出

132

数据范围

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

题解

关于这种不能穿过某条直线的网格行走问题,先讲一下怎么做。
我们知道,答案就等于走到点(n,m)的所有路径数量减去穿过这条直线的路径数量。
我们还知道由点(0,0)走到点(n,m)的路径数为Cn(m)n+m

所以我们现在只需求得穿过这条直线的路径数量就可以知道答案了。
对于一个如下的网格,有这样的一条违法路径穿过了y=x这条直线。

这里写图片描述

易得,不能穿过y=x这条直线就等于不能碰到y=x+1,所以我们找到y=x+1这条直线(图中为棕色直线),并将原路径沿这条直线对称过去(除了最下面的一段,图中为橙色),可以得到下图

这里写图片描述

像这样,路径中点A(n,m)会对称到点B(m-1,n+1),并且从原点走到对称点B(m-1,n+1)的一条路径都可以对称回来,并对应着一条从原点走到终点A(n,m)且穿过y=x直线的路径。

所以穿过直线y=x的路径数就对于从原点走到对称点的路径数。
走到对称点B(m-1,n+1)的路径数SB=Cm1n+1+m1=Cm1n+m
走到原终点A(n,m)的路径数SA=Cmn+m

Ans=SA-SB

=Cmn+m-Cm1n+m

=(n+m)!m!n! (n+m)!(m1)!(n+1)!

=(n+m)!(n+1)m!(n+1)! (n+m)!mm!(n+1)!

=(n+m)!(n+1m)m!(n+1)!
再约一下分,得

Ans=(n+2)(n+3)(n+4)......(n+m)(n+1m)m!

分数线上面的部分我们可以用高精度乘法将积算出来。
那分母怎么处理呢?
我们看到数据范围,惊奇的发现m<=5000,于是我们可以打一个单精度除法除m次。
因为直接打会超时,所以打高精度时要压位。

Code(Pascal)

const    mo=100000000000000;var    lj,dt:array[0..30000] of int64;    n,m,j,i,l:longint;    k,o:int64;procedure cs(o:int64);    var        i,j,l:longint;        hhh:int64;    begin        hhh:=0;        for i:=1 to lj[0] do        begin            dt[i]:=lj[i]*o+hhh;            hhh:=dt[i] div mo;            dt[i]:=dt[i] mod mo;        end;        dt[0]:=lj[0];        while hhh>0 do        begin            inc(dt[0]);            dt[dt[0]]:=hhh mod mo;            hhh:=hhh div mo;        end;        for i:=0 to dt[0] do        lj[i]:=dt[i];    end;procedure cd(o:int64);    var        i,j,l:longint;        hhh:int64;    begin        for i:=lj[0] downto 1 do        begin            dt[i]:=(lj[i]+hhh) div o;            hhh:=(lj[i]-dt[i]*o+hhh)*mo;        end;        dt[0]:=lj[0];        while (dt[dt[0]]=0) and (dt[0]>0) do        dec(dt[0]);        for i:=lj[0] downto dt[0] do        lj[i]:=0;        for i:=0 to dt[0] do        lj[i]:=dt[i];    end;begin    readln(n,m);    lj[0]:=1;    lj[1]:=1;    for i:=n+2 to n+m do    cs(i);    cs(n+1-m);    for i:=1 to m do    cd(i);    write(lj[lj[0]]);    for i:=lj[0]-1 downto 1 do    begin        k:=lj[i];        o:=0;        while k>0 do        begin            inc(o);            k:=k div 10;        end;        for l:=1 to 14-o do        write(0);        write(lj[i]);    end;end.
3 0
原创粉丝点击