Address

Solution

  • 非常有意思,思路非常棒的题目
  • 发现一个很优秀的条件:对于 1in1\le i\le n1jm1\le j\le m 满足 0xi,jm0\le x_{i,j}\le m ,并且对于任意的 1in1\le i\le n1j<m1\le j<m 满足 xi,j<xi,j+1x_{i,j}<x_{i,j+1}
  • 这个条件告诉我们:这个数组每行都是 [0,m][0,m] 内的数扔掉其中一个之后再按顺序排列的
  • m=3m=3xi,jx_{i,j} 一行内的合法方案只有 44 种( 1,2,31,2,30,2,30,2,30,1,30,1,30,1,20,1,2
  • 于是我们把问题转化成了一个序列 ai[0,m](1in)a_i\in[0,m](1\le i\le n) 的计数(即 aia_i 在第 ii 行没有出现过)
  • 而对于 xi,j<xi1,j+1x_{i,j}<x_{i-1,j+1} 的限制,分析一下可以发现,这等价于对于每个 1<in1<i\le n 都有 aiai11a_i\ge a_{i-1}-1
  • 可以假设 a0=0a_0=0 ,那么上面的条件对于 1in1\le i\le n 都满足
  • 我们有了一个 DP 状态: f[i][j]f[i][j] 表示确定到前 ii 个数,第 ii 个数不超过 jj 的方案数( jj 的上界是 m+1m+1 ,原因下面会解释)
  • f[0][0]=1f[0][0]=1
  • f[i][j]=f[i][j1]+f[i1][j+1]f[i][j]=f[i][j-1]+f[i-1][j+1]
  • 意义是第 ii 个数为 jj 时,第 i1i-1 个数必须 j+1\le j+1 ,这个方案再加上第 ii 个数不超过 j1j-1 的方案数
  • 注意到如果 jj 的上界为 mm ,那么这个转移在 j<mj<mj=mj=m 时会有区别,不便于接下来的讨论
  • 答案为 f[n][m+1]f[n][m+1]
  • 我们有了 O(nm)O(nm) 的 DP 之后,我们考虑从 f[0][0]f[0][0] 转移到 f[n][m+1]f[n][m+1] 的过程
  • 假设现在在 f[i][j]f[i][j]
  • 执行第一种转移之后, jj 会加一
  • 执行第二种转移之后, ii 加一, jj 减一
  • 显然这个过程中, jj 不能小于 00 或大于 m+1m+1
  • 把从 f[0][0]f[0][0]f[n][m+1]f[n][m+1] 的转移过程描述成一个 1-111 构成的序列, 1-1 表示第一种转移, 11 表示第二种转移
  • 那么经过前 kk 次转移之后 jj 的值,就等于这个序列的长度为 kk 的前缀和
  • 于是这个序列需要满足任意的前缀和都不能小于 00 或大于 m+1m+1
  • 把问题抽象到二维平面上,答案即为从 (0,0)(0,0) 出发,只能往右或往上走,在 y=xy=xy=x+m+1y=x+m+1 两条直线的夹缝之间(相当于不能触碰直线 y=x1y=x-1y=x+m+2y=x+m+2 )到达 (n,n+m+1)(n,n+m+1) 的方案数
    [BZOJ4005][JLOI2015]骗我呢(组合数学好题)
  • 如果不考虑限制,那么方案数显然为 (2n+m+1n)\binom{2n+m+1}n
  • 答案可以看成
  • (2n+m+1n)\binom{2n+m+1}n-先越过下边界的方案数-先越过上边界的方案数
  • 考虑先越过下边界的方案数。考虑一条 (0,0)(0,0)(n,n+m+1)(n,n+m+1) 的,第一个越过的边界为下边界的路径,如果找到第一个越过下边界的点,将这个点之后的路径(下图中蓝色)关于直线 y=x1y=x-1 翻折(下图中紫色)
    [BZOJ4005][JLOI2015]骗我呢(组合数学好题)
  • 发现这其实就是 (0,0)(0,0)(n+m+2,n1)(n+m+2,n-1) ,第一个越过的边界为下边界的方案数
  • 同理,先越过上边界到达 (n,n+m+1)(n,n+m+1) 的方案数,就是先越过上边界到达 (n1,n+m+2)(n-1,n+m+2) 的方案数
  • 定义两个函数
  • f(x,y)f(x,y) :从 (0,0)(0,0) 开始第一个越过的边界为下边界,到达 (x,y)(x,y) 的方案数
  • g(x,y)g(x,y) :从 (0,0)(0,0) 开始第一个越过的边界为上边界,到达 (x,y)(x,y) 的方案数
  • 注意上面的 (x,y)(x,y) 均满足 yx<0y-x<0yx>m+1y-x>m+1
  • 考虑 f(x,y)f(x,y) :由于 yx<0y-x<0yx>m+1y-x>m+1 ,故 (0,0)(0,0)(x,y)(x,y) 的路径必然越过边界
  • 所以
  • f(x,y)=(x+yx)(x,y)f(x,y)=\binom{x+y}x-先越过上边界到达(x,y)的方案数
  • 而对于一条先越过上边界到达 (x,y)(x,y) 的路径,将这条路径在第一个越过 y=x+m+1y=x+m+1 的位置之后的部分做一个关于直线 y=x+m+2y=x+m+2 的翻折,就得到一条从 (0,0)(0,0) 开始,第一个越过的边界为上边界,到达 (ym2,x+m+2)(y-m-2,x+m+2) 的路径
  • 于是
  • f(x,y)=(x+yx)g(ym2,x+m+2)f(x,y)=\binom{x+y}x-g(y-m-2,x+m+2)
  • 同理
  • g(x,y)=(x+yx)f(y+1,x1)g(x,y)=\binom{x+y}x-f(y+1,x-1)
  • 如要计算一个 f(x,y)f(x,y)g(x,y)g(x,y) ,则可以按照上面的两个式子进行递归求解
  • 边界:当 x<0x<0y<0y<0 时, f(x,y)=g(x,y)=0f(x,y)=g(x,y)=0
  • 可以证明,递归最多 O(n+m)O(n+m) 次可以到达边界
  • 答案为
  • (2n+m+1n)f(n+m+2,n1)g(n1,n+m+2)\binom{2n+m+1}n-f(n+m+2,n-1)-g(n-1,n+m+2)
  • 复杂度 O(n+m)O(n+m)

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

int f(int, int);
int g(int, int);

const int N = 3e6 + 5, ZZQ = 1e9 + 7;

int n, m, fac[N], inv[N];

int C(int n, int m)
{
	return 1ll * fac[n] * inv[m] % ZZQ * inv[n - m] % ZZQ;
}

int f(int x, int y)
{
	if (x < 0 || y < 0) return 0;
	return (C(x + y, x) - g(y - m - 2, x + m + 2) + ZZQ) % ZZQ;
}

int g(int x, int y)
{
	if (x < 0 || y < 0) return 0;
	return (C(x + y, x) - f(y + 1, x - 1) + ZZQ) % ZZQ;
}

int main()
{
	std::cin >> n >> m;
	fac[0] = inv[0] = inv[1] = 1;
	for (int i = 1; i <= (n << 1) + m + 1; i++)
		fac[i] = 1ll * fac[i - 1] * i % ZZQ;
	for (int i = 2; i <= (n << 1) + m + 1; i++)
		inv[i] = 1ll * (ZZQ - ZZQ / i) * inv[ZZQ % i] % ZZQ;
	for (int i = 2; i <= (n << 1) + m + 1; i++)
		inv[i] = 1ll * inv[i] * inv[i - 1] % ZZQ;
	std::cout << ((C((n << 1) + m + 1, n) - f(n + m + 2, n - 1)
		+ ZZQ) % ZZQ - g(n - 1, n + m + 2) + ZZQ) % ZZQ;
	return 0;
}

相关文章:

  • 2022-01-04
  • 2021-06-04
  • 2021-09-13
  • 2022-12-23
  • 2022-12-23
  • 2022-01-30
  • 2021-06-11
  • 2022-12-23
猜你喜欢
  • 2021-08-02
  • 2022-03-03
  • 2021-10-06
  • 2021-12-11
  • 2022-02-25
  • 2022-01-18
  • 2021-06-01
相关资源
相似解决方案