Fibonacci Paths

Fibonacci Paths

图论 + dp

题意简述

给定一个有向图,\(n\) 个顶点,\(m\) 条边。每个顶点 \(v\) 有一个正整数权值 \(a_v\)。
统计所有至少包含两个顶点的简单路径,使得路径上顶点权值依次组成的序列是广义斐波那契数列。

广义斐波那契数列定义:序列 \(x_0, x_1, \dots, x_k\) 满足

  • \(x_0, x_1\) 为任意自然数;
  • 对于 \(i \ge 2\),有 \(x_i = x_{i-1} + x_{i-2}\)。

答案对 \(998244353\) 取模。

关键观察

  1. 路径长度很短
    因为所有 \(a_i \ge 1\),广义斐波那契数列从第三项开始严格递增且增长极快。
    由于 \(a_i \le 10^{18}\),任何合法路径的长度最多约 \(90\)。

  2. 简单路径条件自动满足
    广义斐波那契序列从第三项开始严格递增,因此后面的权值一定大于前面的权值,顶点不可能重复。
    所以题目要求的“简单路径”无需额外判断,只需统计所有有向路径。

  3. 动态规划可行
    设 \(dp[v \to u]\) 表示以有向边 \(v \to u\) 结尾的合法路径数量。
    对于边 \(v \to u\),若存在前驱边 \(w \to v\) 满足

    \[a_w + a_v = a_u \]

    则所有以 \(w \to v\) 结尾的路径都可以接上 \(u\),形成新的合法路径。

DP 转移

对于每条边 \(v \to u\):

\[dp[v \to u] = 1 + \sum_{\substack{w \to v \\ a_w = a_u - a_v}} dp[w \to v] \]

其中 \(1\) 表示路径 \(v \to u\) 本身(长度为 \(2\),一定合法)。

直接枚举所有 \(w\) 会超时,因此为每个顶点 \(v\) 维护一个哈希表 mp[v]:

  • 键:起点权值 \(a_w\);
  • 值:所有以 \(w \to v\) 结尾的路径数量之和,即 \(\sum dp[w \to v]\)。

处理边 \(v \to u\) 时,只需计算目标权值:

\[\text{target} = a_u - a_v \]

若 \(\text{target} > 0\),则从 mp[v] 中取出 target 对应的值累加即可。

处理顺序

由于 \(a_u = a_w + a_v > a_v\),所以边 \(v \to u\) 的终点权值 \(a_u\) 一定大于边 \(w \to v\) 的终点权值 \(a_v\)。
因此,将所有边按照终点的权值 \(a_u\) 从小到大排序,依次处理。
这样在处理边 \(v \to u\) 时,所有可能作为前驱的边 \(w \to v\) 都已经处理完毕,mp[v] 中已经存好了需要的数据。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll MOD = 998244353;

void solve() {
    int n, m; cin >> n >> m;
    vector<ll> weight(n + 1);
    for(int i = 1; i <= n; i ++) cin >> weight[i];
    vector<pair<int, int>> edge;
    for(int i = 1; i <= m; i ++) {
        int u, v; cin >> u  >> v;
        edge.push_back({u, v});
    }

    sort(edge.begin(), edge.end(), [&](auto& a, auto& b){
        return weight[a.second] < weight[b.second];
    });

    ll ans = 0;
    vector<unordered_map<ll, ll>> dp(n + 1);
    for(auto [u, v] : edge) {
        ll cur = 1;
        ll t = weight[v] - weight[u];
        if(t > 0) {
            auto it = dp[u].find(t);
            if(it != dp[u].end()) cur = (cur + it->second) % MOD;
        }

        dp[v][weight[u]] = (dp[v][weight[u]] + cur) % MOD;
        ans = (ans + cur) % MOD;
    }
    cout << ans << "\n";
}

int main() {
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int _ = 1; cin >> _;
    while(_--) solve();
    return 0;
}
posted @ 2026-10-03 10:20  thenrify  阅读(8)  评论(0)    收藏  举报