[JLOI2015]骗我呢

骗我呢

题解

首先我们可以将原题的模型转化一下。
对于矩阵上的每个点,它是比它右边与右上的两个点小的,而总共有 m m m列, m + 1 m+1 m+1个取值。
所以我们知道,对于每一行,它最多存在一个点使得这个点的右边的点比这个点大 2 2 2,其它地方都是大 1 1 1
我们记第 i i i行的这个点的列标号为 p i p_{i} pi,很明显, p p p序列满足 ∀   i < j , p i + i ⩽ p j + j \forall\,i<j, p_{i}+i\leqslant p_{j}+j i<j,pi+ipj+j
我们相当于要求出满足这个条件的序列数。

这就很容易想到 d p dp dp,我们记 d p i , j dp_{i,j} dpi,j表示选到第 i i i个数, p i + i = j p_{i}+i=j pi+i=j时的合法方案数。
有转移式,
d p i , j = ∑ k = i − 1 j d p i − 1 , k dp_{i,j}=\sum_{k=i-1}^{j}dp_{i-1,k} dpi,j=k=i1jdpi1,k
很明显的前缀和结构,转移式再可以被表示为
d p i , j = d p i − 1 , j + d p i , j − 1 dp_{i,j}=dp_{i-1,j}+dp_{i,j-1} dpi,j=dpi1,j+dpi,j1

这不就是杨辉三角了吗?由于只有 j ∈ [ i , i + m ] j\in[i,i+m] j[i,i+m] d p dp dp才有值,所以我们可以联想到卡塔兰数的模型。
我们有两条直线 x − y = 0 x-y=0 xy=0 x − y + m = 0 x-y+m=0 xy+m=0,我们走时不能超出这两条直线,也就是走到 x − y − 1 = 0 x-y-1=0 xy1=0 x − y + m + 1 = 0 x-y+m+1=0 xy+m+1=0上,我们的终点是 ( n + 1 , n + m + 2 ) (n+1,n+m+2) (n+1,n+m+2)
我们分别记这两条直线为 A A A B B B
由于我们可以从 A A A之间 B B B相互走,也就是从 A A A走到 B B B和从 B B B走到 A A A,我们不能直接求走到这两条直线的方案数,考虑容斥。
也就是说我们的答案为
a n s = f − f ( A ) − f ( B ) + f ( A B ) + f ( B A ) − f ( A B A ) − f ( B A B ) + f ( A B A B ) + f ( B A B A ) − . . . ans=f-f(A)-f(B)+f(AB)+f(BA)-f(ABA)-f(BAB)+f(ABAB)+f(BABA)-... ans=ff(A)f(B)+f(AB)+f(BA)f(ABA)f(BAB)+f(ABAB)+f(BABA)...

但这些答案又该如何求呢?
f f f明显是全部从 ( 1 , 1 ) (1,1) (1,1)走到 ( n + 1 , n + m + 2 ) (n+1,n+m+2) (n+1,n+m+2)的方案,也就是 ( 2 n + m + 1 n ) \binom{2n+m+1}{n} (n2n+m+1)
f ( A ) f(A) f(A) f ( B ) f(B) f(B)只要我们将终点沿着 A A A B B B翻折即可。
但对于 A B , B A , A B A , B A B . . . AB,BA,ABA,BAB... AB,BA,ABA,BAB...这些呢?
如果我们现在再直线 A A A上,走到直线 B B B上,相当于我们 x x x要比 y y y多走 m + 2 m+2 m+2步。
假设我们原终点是 ( x , y ) (x,y) (x,y),那么我们此时的新终点应是 ( y − m − 2 , x + m + 2 ) (y-m-2,x+m+2) (ym2,x+m+2)
同理可得从 B B B走到 A A A的新终点是 ( y + 1 , x − 1 ) (y+1,x-1) (y+1,x1)
我们就一直枚举 A B A B A . . . ABABA... ABABA... B A B A B . . . BABAB... BABAB...的路径终点,直到走出去为止。
只需要先将组合数预处理后就可以得到答案了。
时间复杂度 O ( n + m ) O\left(n+m\right) O(n+m)

源码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 3000005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;
const int mo=1e9+7;
const int jzm=2333;
const int lim=10000000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-9;
typedef pair<int,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1LL)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1LL;}return t;}
int n,m,dp[MAXN],ans,fac[MAXN],inv[MAXN],f[MAXN];
void init(){
	fac[0]=fac[1]=inv[0]=inv[1]=f[1]=1;
	for(int i=2;i<=n+n+m+1;i++){
		fac[i]=1ll*i*fac[i-1]%mo;
		f[i]=1ll*(mo-mo/i)*f[mo%i]%mo;
		inv[i]=1ll*f[i]*inv[i-1]%mo;
	}
}
int C(int x,int y){if(x<0||y<0||x<y)return 0;return 1ll*fac[x]*inv[y]%mo*inv[x-y]%mo;}
int sakura1(int x,int y);
int sakura2(int x,int y);
int sakura1(int x,int y){if(x<0||y<0)return 0;return add(C(x+y,x),mo-sakura2(y+1,x-1),mo);}
int sakura2(int x,int y){if(x<0||y<0)return 0;return add(C(x+y,x),mo-sakura1(y-m-2,x+m+2),mo);}
signed main(){
	read(n);read(m);init();int ans=C(n+n+m+1,n);
	ans=add(ans,mo-sakura1(n-1,n+m+2),mo);
	ans=add(ans,mo-sakura2(n+m+2,n-1),mo);
	printf("%d\n",ans);
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值