【BZOJ 3907】【JZOJ 3431】网格

Description

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

Solution

之前做过一道几乎一样的题;

首先不考虑中间那条y=x的线,答案很显然就是 Cmn+m
因为我们是可以走到那条直线上的,很恶心,所以我们就把它往上移一个单位,现在就不能碰到它了,只能在它下面走动,
能我们只要用原答案减去越界的答案即可,
新建点B是以点(n,m)以直线y=x+1为对称轴的对称点,因为从原点到这个点一定是要经过直线y=x+1的,而我们把一些路径沿着直线对称过去后依旧可以到达点(n,m),所以所有从原点到点(n,m)的所有不合法的路径个数就是从原点到点B的路径个数(懒得上图,大家凑合着理解吧),
这题要用高精度,把C公式的上下分解质因数以后约分即可,

Code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define cq c[q]
#define cw c[w]
#define c0 c[0]
#define c3 c[3]
#define read(a) scanf("%d",&a)
using namespace std;
typedef long long LL;
const int N=5500,maxlongint=2147483640,mo=10,mo1=1;
int m,n,ans;
int b[N*2];
int c[4][N];
void fen(int q,int e)
{fo(i,2,sqrt(q))while(q%i==0)b[i]+=e,q/=i;if(q!=1)b[q]+=e;}
void chen(int q,int w,int e)
{
    fo(i,1,c0[0])c0[i]=0;
    c0[0]=cq[0]+cw[0]-1;
    fo(i,1,cq[0])
        fo(j,1,cw[0])
        {
            c0[i+j-1]+=cw[j]*cq[i];
            c0[i+j]+=c0[i+j-1]/mo;
            c0[i+j-1]%=mo;
        }
    while(c0[c0[0]+1])
    {
        c0[0]++;
        c0[c0[0]+1]+=c0[c0[0]]/mo;
        c0[c0[0]]%=mo;
    }
    fo(i,0,c0[0])c[e][i]=c0[i];
}
void ksm(int q,int w)
{
    c3[0]=0;c[2][0]=c[2][1]=1;
    while(q)c3[++c3[0]]=q%mo,q/=mo;
    while(w)
    {
        if(w%2)chen(2,3,2);
        chen(3,3,3);
        w/=2;
    }
}
int main()
{
    int q,w;
    read(n),read(m);
    if(!n){printf("1\n");return 0;}
    n+=m;
    fen(n-2*m+1,1);
    fo(i,m+1,n)fen(i,1);
    fo(i,2,n-m+1)fen(i,-1);
    c[1][0]=c[1][1]=1;
    fo(i,1,n)if(b[i])
    {
         ksm(i,b[i]);
         chen(1,2,1);
    }
    fod(i,c[1][0],1)printf("%d",c[1][i]);
    return 0;
}

你可能感兴趣的:(高精度,组合数)