[JZOJ4236] 登山

题目描述

恶梦是一个登山爱好者,今天他来到了黄山。
俗话说的好,不走回头路。所以在黄山,你只能往前走,或者往上走。并且很显然的是,当你走到山脊的时候,你不能够往上走,你只能往前走一步再往上走。
抽象一点而言就是,你可以把黄山视为一个N * N格点图,恶梦从(0,0)开始出发,要走到(N,N)。当他走到位置(x,y)的时候,它可以往(x + 1,y),或(x,y+1)走。
并且当他走到(x,x)的时候,由于他已经处在了山脊上,所以他不能够往(x,x+1)方向上走。
当恶梦兴致勃勃准备开始爬山的时候,他的同伴告诉他,黄山由于年久失修,有一些位置出现了大坑,不能走。恶梦觉得更刺激了,但他想先知道他能有多少种方式走到黄山顶。
由于这个数字很大,所以你只需要将答案对10^9 + 7取模输出即可。
对于30%的数据,保证N<=5000
对于另外20%的数据,保证C=0
对于另外20%的数据,保证C=1
对于100%的数据,保证N<=100000,C<=1000
保证对于(0,0),(N,N)不存在障碍点。

解题思路

如果没有山脊的限制就十分简单了嘛。
假如没有,设f[i]表示恰好走到第i个障碍点的合法路径条数,即除了这个点,路径其他点碰不到障碍。然后f[i]可以由能够到达他的障碍点j的f[j]容斥出来,即在不合法路径经过的第一个障碍点j处容斥掉不合法答案。
考虑有山脊的限制怎么办。实际上,我们如果要从(a,b)到达(c,d),不碰到直线到(c,d)的方案数=随便走到(c,d)的方案数-随便走到(c,d)关于直线的对称点的方案数。
可以发现,任意一条走到对称点的路径一定会碰到直线,其第一个碰到直线的点后面的路径对称回去,恰好一一对应不合法的到(c,d)的路径。
那么就很好算了。
这里写图片描述

代码

不是同一道题,不过大体一样,多了一些删去没用的障碍点的操作。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
typedef double db;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
#define cmax(a,b) (a=(a>b)?a:b)
#define cmin(a,b) (a=(a<b)?a:b)
const int N=5e6+5,mo=1e9+7;
struct rec
{
    int x,y;
};
bool operator <(rec a,rec b)
{
    return a.x<b.x||a.x==b.x&&a.y<b.y;
}
multiset<rec> tr;
int fac[N],rev[N],i,j,n,m,t,C,X[3005],Y[3005],id[3005],f[3005],t1;
bool cmp(int x,int y)
{
    return X[x]<X[y]||X[x]==X[y]&&Y[x]<Y[y];
}
int ksm(int x,int y)
{
    int ret=1;
    while (y)
    {
        if (y&1) ret=1ll*ret*x%mo;
        y>>=1;
        x=1ll*x*x%mo;
    }
    return ret;
}
void predo(int n)
{
    fac[0]=1;
    fo(i,1,n) fac[i]=1ll*i*fac[i-1]%mo;
    rev[n]=ksm(fac[n],mo-2);
    fd(i,n,1) rev[i-1]=1ll*i*rev[i]%mo;
}
int vert(int &x,int &y)
{
    y--;x++;
    swap(x,y);
}
int c(int n,int m)
{
    if (n==m) return 1;
    if (n>m||m<0||n<0) return 0;
    return 1ll*fac[m]*rev[n]%mo*rev[m-n]%mo;
}
int ways(int ax,int ay,int bx,int by)
{
    int ret=0;
    ret=c(bx-ax,bx-ax+by-ay);
    vert(bx,by);
    ret=(ret-c(bx-ax,bx-ax+by-ay)+mo)%mo;
    return ret;
}
int main()
{
    scanf("%d %d",&n,&t1);
    m=n;
    predo(5e6);
    fo(i,1,t1)
    {
        t++;
        scanf("%d %d",X+t,Y+t);
        if (X[t]<0||Y[t]>m||X[t]>n||Y[t]>X[t]||tr.find({X[t],Y[t]})!=tr.end()) t--;
        else tr.insert({X[t],Y[t]});
    }
    t++;
    X[t]=n;Y[t]=m;
    fo(i,1,t) id[i]=i;
    sort(id+1,id+1+t,cmp);
    fo(i,1,t)
    if (X[id[i]]>=0) 
    {
        f[i]=ways(0,0,X[id[i]],Y[id[i]]);
        fo(j,1,i-1) if (X[id[j]]>=0&&X[id[j]]<=X[id[i]]&&Y[id[j]]<=Y[id[i]])
            f[i]=(f[i]-1ll*f[j]*ways(X[id[j]],Y[id[j]],X[id[i]],Y[id[i]])%mo+mo)%mo;
    }
    printf("%d\n",f[t]);
}

猜你喜欢

转载自blog.csdn.net/zltjohn/article/details/79883367