D. Fibonacci Paths 详细解题思路
核心问题与数学性质
本题要求找出有向图中所有长度 $k \ge 2$ 的简单路径 $v_0 \to v_1 \to \dots \to v_k$,使得路径上的顶点值序列 $x_0, x_1, \dots, x_k$ 构成广义斐波那契数列。
广义斐波那契定义
$$ x_i = x_{i-2} + x_{i-1} \quad \text{for } i \ge 2 $$关键性质:隐式拓扑序
由于题目保证所有顶点值 $a_v \ge 1$,对于任何长度 $\ge 3$ 的合法路径,必然满足:
这意味着路径上的点权值是严格递增的(除了前两个点可能大小任意)。
- 这一性质保证了我们不需要担心“环”的问题(除了长度为 2 的路径)。
- 这也提示我们可以按照点权值 $a_v$ 从小到大的顺序来处理边,这相当于在一个隐式的 DAG(有向无环图)上进行动态规划。
1. 动态规划状态定义
我们需要知道路径的“末端”和“倒数第二个值”才能确定下一个值需要是多少。
- 状态定义 $DP[v][\text{pre}]$:
表示以顶点 $v$ 为终点,且路径上倒数第二个顶点的值**为 $\text{prev_val}$ 的合法斐波那契路径的总数。 - 数据结构:
由于 $a_v$ 范围可达 $10^{18}$,且对于每个点 $v$,有效的入边数量有限,我们使用std::map<long long, long long>来存储每个点的 DP 状态,避免空间爆炸。
2. DP 转移与核心逻辑
我们按照边的终点权值 $a_v$ 对所有边进行排序。排序是为了保证当我们处理到点 $v$ 时,所有可能作为 $v$ 前驱的路径(权值更小的点)都已经计算完毕。
转移步骤(处理边 $u \to v$)
假设当前处理的边是 $u \to v$,当前路径的最后两项确定为 $[\dots, a_u, a_v]$。
基础贡献(长度 $k=2$):
边 $(u, v)$ 本身构成一条合法路径 $[a_u, a_v]$(题目允许前两项任意)。路径扩展(长度 $k \ge 3$):
我们需要寻找一个前驱点 $prev$,使得路径 $prev \to u \to v$ 满足斐波那契性质。- 条件:$av = a{prev} + a_u$
所需前驱值:$\text{needed} = a_v - a_u$
如果 $\text{needed} > 0$,我们查询 $u$ 的 DP 表,看有多少条以 $u$ 结尾且倒数第二个值为 $\text{needed}$ 的路径。
状态更新:
- 更新总答案:$\text{TotalAns} = (\text{TotalAns} + \text{count}) \pmod{MOD}$
- 更新 DP 表:这条新路径现在以 $v$ 结尾,倒数第二个值是 $a_u$。
复杂度分析
- 排序:$O(M \log M)$
- DP 转移:遍历 $M$ 条边,每次在 Map 中查询和插入,耗时 $O(\log (\text{degree}))$ 或 $O(\log M)$。
- 总复杂度:$O(M \log M)$,在 $M=2 \cdot 10^5$ 下完全可接受。
3. C++ 代码实现
1 |
|

