[JZOJ3431]【GDOI2014模拟】网格

Description

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

Solution

不能穿过 y=x 这条线,相当于不能碰到 y=x+1 这条线

然后我们取终点关于 y=x+1 对称的点。

可以发现,对于每条到对称点的路径,都碰到 y=x+1 ,并且都能关于 y=x+1 对称出一条到终点的路径。
或者说,它们是一一对应的。

所以答案就是随便走到终点的路径-随便走到对称点的路径。

然后再用组合数高精度之类的搞搞就好了。

Code

高精度码量略大

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
#define mx 100000000
using namespace std;
struct arr
{
    long long a[2500];
};
arr a,k;
int n,m;
arr pl(arr a,arr b)
{
    int la,lb,lc,i;
    arr c;
    la=a.a[0];
    lb=b.a[0];
    memset(c.a,0,sizeof(c.a));
    fo(i,1,max(la,lb))
    {
        c.a[i]=0;
        c.a[i]=c.a[i]+a.a[i]+b.a[i];
        c.a[i+1]=c.a[i]/mx;
        c.a[i]=c.a[i]%mx;
    }
    lc=max(la,lb);
    if (c.a[lc+1]!=0) lc++;
    while(c.a[lc]==0&&lc>1) lc--;
    c.a[0]=lc;
    return c;
}
arr ti(arr a,int b)
{
    int la,lc,i,j;
    arr c;
    la=a.a[0];
    memset(c.a,0,sizeof(c.a));
    j=0;
    fo(i,1,la)
    {
        c.a[i]=j+a.a[i]*b;
        j=c.a[i]/mx;
        c.a[i]=c.a[i]%mx;
    }
    c.a[la+1]=j;
    lc=a.a[0]+9;
    while(c.a[lc]==0&&lc>1) lc--;
    c.a[0]=lc;
    return c;
}
arr t2(arr a,arr b)
{
    int la,lb,lc,i,j;
    arr c;
    la=a.a[0];
    lb=b.a[0];
    memset(c.a,0,sizeof(c.a));
    fo(i,1,lb)
    {
        fo(j,1,la)
        {
            c.a[i+j-1]=c.a[i+j-1]+b.a[i]*a.a[j];
            c.a[i+j]=c.a[i+j]+c.a[i+j-1]/mx;
            c.a[i+j-1]=c.a[i+j-1]%mx;
        }
    }
    lc=la+lb-1;
    if (c.a[lc+1]!=0) lc++;
    while(c.a[lc]==0&&lc>1) lc--;
    c.a[0]=lc;
    return c;
}
int larger(arr a,arr b)
{
    if (a.a[0]>b.a[0]) return 1;
    if (a.a[0]<b.a[0]) return -1;
    {
        int i;
        fod(i,a.a[0],1)
        {
            if (a.a[i]>b.a[i]) return 1;
            if (a.a[i]<b.a[i]) return -1;
        }
        return 0;
    }
}
arr div(arr a,int k)
{
    int i,la=a.a[0];
    fod(i,la,2)
    {
        if (a.a[i]%k!=0) a.a[i-1]+=(a.a[i]%k)*mx;
        a.a[i]=a.a[i]/k;
    }
    a.a[1]=a.a[1]/k;
    if (a.a[la]==0) a.a[0]--;
    return a;
}
arr turn(int i)
{
    int j;
    arr c;
    c.a[0]=floor(log(i)/log(mx))+1;
    fo(j,1,c.a[0])
    {
        c.a[j]=i%mx;
        i/=mx;
    }
    return c;
}
int main()
{
    freopen("map.in","r",stdin);
    int i;
    cin>>n>>m;
    a.a[0]=a.a[1]=1;
    fo(i,n+2,n+m) 
    {
        a=ti(a,i); 
    } 
    a=ti(a,n+1-m);
    fo(i,2,m) 
        a=div(a,i);
    fod(i,a.a[0],1) 
    {
        int p=mx/10;
        while(a.a[i]<p&&p>1&&i!=a.a[0])
        {
            printf("0");
            p/=10;
        }
        printf("%d",a.a[i]);
    }
    cout<<endl; 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值