[JLOI2015] 骗我呢
Link
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\) 翻。
对于此题,\(l_1: y=x-(m+2)\) 以及 \(l_2:y=x+1\),最终到达 \((n+m+1,n)\)
#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);
}