ACM3759 二维跑步 题解
题目分析
这道题是二维空间上的随机游走。基本思路是:
- 观察转移规则,发现从任意
出发,向右/原地/向左的选择数都是
,与
无关
- 由此直接写出总量的生成函数
- 转化为普通多项式后用三项递推
求出所有系数
最终复杂度 。
数学分析
设 为走
步后落在
处的总方案数(
任意)。观察题目给出的转移规则:
| 当前位置 | 向右 |
原地 |
向左 |
|---|---|---|---|
| 到全部 3 个 |
保持 |
到 |
|
| 到全部 3 个 |
保持 |
到 |
|
| 到全部 3 个 |
保持 |
到 |
关键观察:无论当前在哪个 ,向右走都有 3 种选择,原地有 1 种,向左有 2 种。因此
的递推与
无关:
定义生成函数 ,上式等价于:
初始 (只有
),故:
问题转化为:求 中
到
的系数之和。
化为普通多项式
令 ,
。则:
位置 的方案数恰好是
。答案即为:
三项递推式
令 。对两边求导:
两边同乘 :
将 ,
,以及
代入,比较
的系数:
整理得:
初始条件
(所有因子都取常数项
)
(多项式无负次项)
时:
由这三项递推式,可以 一次性算出全部
,无需任何迭代 DP。
代码
#include <iostream>
#include <vector>
using namespace std;
const int MOD = 998244353;
int mod_pow(int base, int exp)
{
int result = 1;
while (exp) {
if (exp & 1) result = (long long)result * base % MOD;
base = (long long)base * base % MOD;
exp >>= 1;
}
return result;
}
void precompute_inverses(int n, vector<int> &inv)
{
inv[1] = 1;
for (int i = 2; i <= n; ++i)
inv[i] = (long long)(MOD - MOD / i) * inv[MOD % i] % MOD;
}
vector<int> compute_coefficients(int n, const vector<int> &inv)
{
vector<int> c(2 * n + 1, 0);
c[0] = mod_pow(2, n);
if (n == 0) return c;
c[1] = (long long)n * c[0] % MOD * inv[2] % MOD;
for (int j = 1; j < 2 * n; ++j) {
int term1 = (long long)(6LL * n - 3LL * j + 3) * c[j - 1] % MOD;
int term2 = (long long)(n - j) * c[j] % MOD;
int numerator = (term1 + term2) % MOD;
if (numerator < 0) numerator += MOD;
c[j + 1] = (long long)numerator * inv[2 * (j + 1)] % MOD;
}
return c;
}
int compute_answer(const vector<int> &c, int n, int m)
{
int ans = 0;
int lo = max(0, n - m);
int hi = min(2 * n, n + m);
for (int j = lo; j <= hi; ++j)
ans = (ans + c[j]) % MOD;
return ans;
}
int main()
{
int n, m;
cin >> n >> m;
vector<int> inv(4 * n + 2);
precompute_inverses(4 * n + 1, inv);
vector<int> c = compute_coefficients(n, inv);
cout << compute_answer(c, n, m) << endl;
return 0;
}
复杂度分析
| 步骤 | 时间 | 空间 |
|---|---|---|
| 预处理逆元 | ||
| 递推求 |
||
| 区间求和 | ||
| 总计 |
- 递推式每步只需两次乘法、一次加法、一次除法(乘逆元),常数极小
- 所需的模逆元
inv[1..4n]可以线性预处理
- 相比于迭代 DP 的
,此方法可以直接处理
高达
级别的数据

京公网安备 11010502036488号