「JLOI2015」骗我呢

由题意,每一行内的数单调递增。又因为 0ai,jm 限制了这些数的取值范围。

那么我们相当于在 m+1 个数中选 m 个数。必然有两个之间相差 2,其余的数连续。

我们设 fi,j 表示第 i 行中,被舍弃掉的数是 j 的方案数。

那么则有

fi,j=k=0j+1fi1,k=k=0jfi1,k+fi1,j+1=fi,j1+fi1,j+1

这个式子看上去是一个表格中的递推,我们将其画到网格图中(第 i 行第 j 列):

这个样子很丑,我们尽量将转移变成水平/垂直的。

虚线为原本的转移,将其改成向上再向右。

这样就有一点像计数问题了。

我们在两侧加上两条直线:

不难发现,结果即为从 (0,0)(n+m,n) ,不经过直线 A:y=x+1B:y=xm2 的路径总数。

考虑用组合数算这个东西。我们定义由 AB 组成的序列表示先经过 AB 的方案。如 AABABBAB

我们发现,连续经过同一条直线没有影响,所以把所有的相同部分全部缩成一个。

显然,它要么以 A 开头,要么以 B 开头。

我们直接用总方案数 ( (2n+m+1n) ) - A 开头的方案数 - B 开头的方案数。

但是后两者又该怎么计算呢?

我们使用对称。

我们将直线 B 关于直线 A 对称过去。过 A 再过 B 实质上就是不停的跨越直线的过程。我们一直对称下去。当我们发现,对称过去的某个点在回来之后并不属于第一象限了,说明这条路径不合法。

容斥。A 开头相当于 A+ABBABAB+ABA+ABABB 开头同理。

我们每次对称都必然会使得距离加一,所以最终一定能找到答案。

#include <cstdio>
const int ccf = 1e9 + 7, N = 3e6 + 5;
int n, m, nn, fac[N], inv[N];
void init() {
    fac[0] = fac[1] = inv[0] = inv[1] = 1;
    nn = n * 2 + m + 1;
    for (int i = 2; i <= nn; i++) fac[i] = 1ll * fac[i - 1] * i % ccf;
    for (int i = 2; i <= nn; i++) inv[i] = 1ll * (ccf - ccf / i) * inv[ccf % i] % ccf;
    for (int i = 2; i <= nn; i++) inv[i] = 1ll * inv[i] * inv[i - 1] % ccf;
}
int C(int n, int m) { return 1ll * fac[n] * inv[m] % ccf * inv[n - m] % ccf; }
int Mir(int x, int y, int dir) {
    if (x < 0 || y < 0) return 0;
    int delta = dir ? -m - 2 : 1;
    return (C(x + y, y) - Mir(y + delta, x - delta, dir ^ 1) + ccf) % ccf;
}
int main(void) {
    scanf("%d%d", &n, &m);
    init();
    int ans = C(nn, n);
    ans = (ans - Mir(n - 1, n + m + 2, 0) + ccf) % ccf;
    ans = (ans - Mir(n + m + 2, n - 1, 1) + ccf) % ccf;
    printf("%d\n", ans);
    return 0;
}
posted @   LewisLi  阅读(97)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示
主题色彩