【YBT2023寒假Day5 C】路径计数(数学)(生成函数)
路径计数
题目链接:YBT2023寒假Day5 C
题目大意
有一个 h*w 的网格,你要从左上角走到右下角,每次可以向右或者向下,定义一条路径的分数是它经过的位置的权值和。
每次会选择权值大的点走过去,如果权值相同,就走右边,如果只有一个方向可以走就走那个方向。
一直左上角权值为 0,其它格子值域 0~S,问你有多少个网格满足路径的分数恰好是 S。
思路
考虑到网格中有很多的地方是没有限制的,而这个数量你会发现并不是固定的。
考虑到移动可以因此分为几类。
(没有限制的向右走,没有限制的向下走,有限制的往限制的方向走)
不过会注意到其实最后会有限制,但两种限制其实不同,考虑分别求贡献加起来。
假设接触了下边界,碰的位置是 ,也就是已经往右走了 次。
那往下走的次数 ,最后限制的走要走 次。
考虑到没有限制的操作会固定一个格子的值,限制一个格子的取值,而有限制的操作会固定一个格子的值。
那任意填数的格子数
然后考虑每种操作分开来 DP 或者什么的,就比如无限制往右走,那就是 为进行 次这个操作,权值和是 的方案数。
(这里就是把 拆成 个非负整数的所有方案,这 个数各自加一的乘积之和)
(加一是因为相等的时候往右走,可以相等)
那无限制往右走就是 为把 拆成 个非负整数的所有方案,这些数的乘积之和。
而有限制的,就是 为把 拆成 个非负整数的方案数。
那总答案就是:(注意组合数是 是因为最后一步一定是 )
但是这还不够,考虑后面枚举的部分要怎么处理。
考虑到这个 DP 都很简单,考虑用看看转移式:
发现是卷积的形式。
然后我们尝试生成函数:
然后弄封闭形式:(错位相减,就只写 的例子了)
然后这个是 ,然后同样的道理(或者这个经典的形式),就得到
那 , 同理有
然后放进后面枚举的式子里面:
其实就是要:
不过 是 的封闭形式,那这个就是 个卷积起来。
那在组合意义上,它的 项,就是用 个非负整数组成 的方案数。
那这个我们用插板法,就是
代码
#include<cstdio>
#define ll long long
#define mo 1000000007
using namespace std;
const int N = 8e6;
int n, m, s, jc[N], inv[N], invs[N];
int add(int x, int y) {return x + y >= mo ? x + y - mo : x + y;}
int dec(int x, int y) {return x > y ? x - y : x - y + mo;}
int mul(int x, int y) {return 1ll * x * y % mo;}
int ksm(int x, ll y) {
int re = 1;
while (y) {
if (y & 1) re = mul(re, x);
x = mul(x, x); y >>= 1;
}
return re;
}
int C(int n, int m) {
if (n < 0 || n < m || m < 0) return 0;
return mul(mul(jc[n], invs[m]), invs[n - m]);
}
int main() {
freopen("count.in", "r", stdin);
freopen("count.out", "w", stdout);
jc[0] = 1; for (int i = 1; i < N; i++) jc[i] = mul(jc[i - 1], i);
inv[0] = inv[1] = 1; for (int i = 2; i < N; i++) inv[i] = mul(inv[mo % i], mo - mo / i);
invs[0] = 1; for (int i = 1; i < N; i++) invs[i] = mul(invs[i - 1], inv[i]);
scanf("%d %d %d", &n, &m, &s);
if (n == 1 && m == 1) {printf("%d", (s == 0) ? 1 : 0); return 0;}
if (n == 1) {printf("%d", C(m - 1 + s - 1, s)); return 0;}
if (m == 1) {printf("%d", C(n - 1 + s - 1, s)); return 0;}
int ans = 0;
for (int a = 0; a < m - 1; a++) {
int b = n - 1, c = m - 1 - a;
ll d = 1ll * n * m - 1 - 2 * (a + b) - c;
ans = add(ans, mul(ksm(s + 1, d), mul(C(a + b - 1, a), C(s - b + 2 * a + 2 * b + c - 1, 2 * a + 2 * b + c - 1))));
}
for (int b = 0; b < n - 1; b++) {
int a = m - 1, c = n - 1 - b;
ll d = 1ll * n * m - 1 - 2 * (a + b) - c;
ans = add(ans, mul(ksm(s + 1, d), mul(C(a + b - 1, b), C(s - b + 2 * a + 2 * b + c - 1, 2 * a + 2 * b + c - 1))));
}
printf("%d", ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现