[JLOI2015] 骗我呢

Description

求从 \((0,0)\) 走到 \((n,m)\) 而不经过某两条形如 \(y=x+b\) 的直线的方案数。

Solution

\(A\) 表示经过 \(l_1\) 的事件,\(B\) 表示经过 \(l_2\) 的事件。首先,连续两次 \(A\)\(B\),可以只看第一次。所以一条不合法路径对应 \(ABAB\dots\)\(BABA\dots\) 的一个前缀。

所以方案是 \(\binom{n+m}{n}-以 A 开头的路径-以 B 开头的路径\)。以 \(A\)\(B\) 开头是类似的,我们只考虑 \(A\) 开头。类似卡特兰数求法,把 \((n,m)\) 沿着 \(l_1\) 对称,得到 \((n',m')\)\(\binom{n'+m'}{n'}\) 就是至少包含一次 \(A\) 的方案数。这个方案包含了以 \(A\)\(BA\) 为前缀的所有路径,所以需要减去 以 \(BA\) 为前缀的路径。再把 \((n',m')\) 沿着 \(l_2\) 对称,这次求出的方案就包含了以 \(BA\)\(ABA\) 为前缀的路径,所以再加上 \(ABA\)……。一直递归下去,每翻折两次,就会使得 \((n,m)\) 变为 \((n+k,m-k)\),当其中一维小于 \(0\) 的时候,就不存在一种映射使得它对应于原路径,求出来的就是无效值。所以递归是有界的,且至多递归 \(O(\frac{n}{k})\) 次。\(B\) 的情况同理,只是先沿着 \(l_2\) 翻。

#include<stdio.h>

typedef long long ll;

const int N=3e6+7;
const int Mod=1e9+7;

int n,m;
ll fac[N],ifac[N],inv[N],ans=0;

inline ll C(int x,int y){return x<y? 0:fac[x]*ifac[y]%Mod*ifac[x-y]%Mod;}

inline void swap(int &x,int &y){x^=y^=x^=y;}
inline void fold(int &x,int &y,bool typ,int op){
    swap(x,y); int dt=typ? (m+2):-1;
    x+=dt,y-=dt; ans=(ans+Mod+op*(x>=0&&y>=0? C(x+y,x):0))%Mod;
}

int main(){
    scanf("%d%d",&n,&m);
    fac[0]=inv[1]=ifac[0]=1; for(int i=1;i<N;i++) fac[i]=fac[i-1]*i%Mod;
    for(int i=2;i<N;i++) inv[i]=(Mod-Mod/i)*inv[Mod%i]%Mod;
    for(int i=1;i<N;i++) ifac[i]=ifac[i-1]*inv[i]%Mod; ans=C(n*2+m+1,n);
    int x=n+m+1,y=n; while(x>=0&&y>=0) fold(x,y,0,-1),fold(x,y,1,1);
    x=n+m+1,y=n; while(x>=0&&y>=0) fold(x,y,1,-1),fold(x,y,0,1);
    printf("%lld",ans);
}
原文地址:https://www.cnblogs.com/wwlwQWQ/p/15543277.html