BZOJ4005: [JLOI2015]骗我呢【卡特兰直线问题】

题目描述:

n × m n\times m n×m的网格,每个格子填 [ 0 , m ] [0,m] [0,m]之一的整数,要求 x i , j < x i , j + 1 , x i , j < x i − 1 , j + 1 x_{i,j}<x_{i,j+1},x_{i,j}<x_{i-1,j+1} xi,j<xi,j+1xi,j<xi1,j+1。问填数方案数。
n , m ≤ 1 0 6 n,m\le10^6 n,m106,mod 109+7

题目分析:

毒瘤神题。
经过一堆神乎其技的转化(详见这位dalao的博客
我们得到了这样一张图(摘自上面的博客)在这里插入图片描述
题目的答案就是从 ( 0 , 0 ) (0,0) (0,0)不触碰两条直线走到 ( n , n + m + 1 ) (n,n+m+1) (n,n+m+1)的方案数。

我们知道从(0,0)走到一个点(i,j)的方案数是 C i + j i C_{i+j}^i Ci+ji,并且可以通过翻折求出必经过一条直线到达一个点的方案数。
譬如(x,y),沿着直线y=x+1翻折,就相当于先下移1,再交换横纵坐标(沿y=x翻折的规律),再上移1,变为(y-1,x+1)。那么必经直线y=x+1到达点(x,y)的方案数就等于到达(y-1,x+1)的方案数。
同理,沿着直线y=x-(m+2)翻折,相当于先上移(m+2),再交换横纵坐标,再下移(m+2),变为(y+m+2,x-(m+2))。

那么我们尝试用到达(n,n+m+1)的方案减去必经上面的直线到达它的方案,再减去必经下面的直线到达它的方案。
但是这样会减多,因为一条路径可能多次经过了两条直线。
那么我们对方案进行分类,减去先经过上面的直线到达它的方案,以及先经过下面的直线到达它的方案。

先经过上面的直线到达它的方案怎么求呢,我只会求必经直线到达它的方案啊QWQ。

考虑用必经上面的直线到达它的方案数再减去这些方案中先经过下面的直线到达已翻折过的点的方案数,求先经过下面的直线到达已翻折过的点的方案数就是一个子问题了。
于是做法就是不断将点(n,n+m+1)沿y=x+1和y=x-(m+2)翻折并减去或加上对应的方案数,直到点的横纵坐标为负数,方案数就始终为0,不必再减了。

Code:

#include<cstdio>
#include<algorithm>
#define maxn 3000005
using namespace std;
const int mod = 1e9+7;
int n,m,fac[maxn],inv[maxn],ans;
void Pre(const int N){
    fac[0]=fac[1]=inv[0]=inv[1]=1;
    for(int i=2;i<=N;i++) fac[i]=1ll*fac[i-1]*i%mod,inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
    for(int i=2;i<=N;i++) inv[i]=1ll*inv[i]*inv[i-1]%mod;
}
inline int C(int n,int m){return n<m||m<0?0:1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;}
inline void flip1(int &x,int &y){swap(x,y),x--,y++;}
inline void flip2(int &x,int &y){swap(x,y),x+=m+2,y-=m+2;}
int main()
{
	scanf("%d%d",&n,&m),Pre(n+m+1+n),ans=C(n+m+1+n,n);
	for(int x=n+m+1,y=n;x>=0&&y>=0;){
		flip1(x,y),ans=(ans+mod-C(x+y,x))%mod;
		flip2(x,y),ans=(ans+C(x+y,x))%mod;
	}
	for(int x=n+m+1,y=n;x>=0&&y>=0;){
		flip2(x,y),ans=(ans+mod-C(x+y,x))%mod;
		flip1(x,y),ans=(ans+C(x+y,x))%mod;
	}
	printf("%d\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值