Evil Shortest Path

Evil Shortest Path

题意

给定有向图,边上有数字 \(c \in [1,9]\)。从顶点 \(1\) 出发,沿边移动时把经过的数字依次拼接成一个十进制整数,称为该次移动的分数。

对每个 \(v = 2,3,\dots,N\),求从 \(1\) 到 \(v\) 的所有移动中,分数最小的那条路径的分数,对 \(998244353\) 取模。

图保证从 \(1\) 可达所有点。同一顶点或边可重复经过。

核心观察

观察 1:长度优先

每条边上的数字 \(\in [1,9]\),没有前导零。因此:

  • 边数为 \(L\) 的路径,分数至少为 \(10^{L-1}\);
  • 边数为 \(L-1\) 的路径,分数至多为 \(10^{L-1}-1\)。

所以边数更少的路径,分数一定更小。第一关键字是路径边数,也就是无权图最短路长度。

观察 2:同长度下按字典序

如果两条路径边数相同,那么分数的大小关系就等价于它们拼接出的数字串的字典序。

因此第二关键字是数字串的字典序。

算法

第一步:BFS 求最短边数

因为每条边的长度都是 \(1\),用 BFS 求出

\[dist[v] = \text{从 } 1 \text{ 到 } v \text{ 的最少边数} \]

并把所有点按 \(dist\) 分层:第 \(d\) 层包含所有 \(dist[v] = d\) 的点。

第二步:按层 DP

按 \(d = 1,2,\dots\) 的顺序处理每一层。

对于第 \(d\) 层的点 \(v\),它的最优路径必然来自第 \(d-1\) 层的某个前驱 \(u\),再走边 \(u \to v\)(数字 \(c\))。此时路径串为 \(\text{str}[u] + c\)。

因为所有前驱 \(u\) 都在第 \(d-1\) 层,它们的串长度相同,所以比较两个候选 \(\text{str}[u_1]+c_1\) 与 \(\text{str}[u_2]+c_2\) 只需:

  1. 先比 \(\text{str}[u_1]\) 与 \(\text{str}[u_2]\),即上一层的字典序排名 \(\mathrm{rank}[u_1]\) 与 \(\mathrm{rank}[u_2]\);
  2. 相等再比 \(c_1\) 与 \(c_2\)。

于是对每个 \(v\),在反向图中枚举入边 \(u \to v\),选出满足 \(dist[u] = dist[v]-1\) 且键

\[(\mathrm{rank}[u],\ c) \]

最小的前驱 \(u^\*\) 与对应数字 \(c^\*\),然后:

\[ans[v] = (ans[u^\*] \cdot 10 + c^\*) \bmod 998244353 \]

最后对当前层所有点按 \(( \mathrm{rank}[u^\*], c^\* )\) 排序,重新分配层内 \(rank\):

  • 若两个点的 \((\mathrm{rank}[u^\*], c^\*)\) 完全相同,它们的最优串相同,\(rank\) 相同;
  • 否则后者 \(rank\) 加一。

正确性

长度优先的正确性

由观察 1,任何长度更短的路径分数都更小,因此 BFS 分层的顺序就是路径分数的第一关键字顺序,按层处理不会错过全局最优。

字典序比较的正确性

上一层所有点的最优串长度都相同(都等于 \(d-1\))。比较 \(\text{str}[u_1]+c_1\) 与 \(\text{str}[u_2]+c_2\),先比前缀部分,即比较 \(\mathrm{rank}[u_1]\) 与 \(\mathrm{rank}[u_2]\);相等再比最后一位 \(c\)。这与字符串字典序比较完全等价。

rank 的一致性

同一层内,\(rank\) 严格按照当前层所有点的最优键 \(( \mathrm{rank}[u^\*], c^\* )\) 排序后从 \(1\) 递增分配,相等的键获得相等的 \(rank\)。因此对于下一层,比较两个点的 \(rank\) 就等价于比较它们的最优串字典序。

模运算的正确性

选择最优前驱完全由 \(rank\) 与 \(c\) 决定,与模运算无关;\(ans\) 只在「已经选定最优前驱之后」用 \(ans[u] \cdot 10 + c \bmod 998244353\) 递推,得到的就是那条最优路径分数的模。

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int>;
using pll = pair<int, ll>;
using tpi = tuple<int, int, int>;
const int INF = 1e8;
const ll MOD = 998244353;

void solve() {
    int n, m; cin >> n >> m;
    vector<vector<pii>> g(n + 1, vector<pii>());
    vector<vector<pii>> grev(n + 1, vector<pii>());
    for(int i = 0; i < m; i ++) {
        int a, b, c; cin >> a >> b >> c;
        if(a == b) continue;
        g[a].push_back({b, c});
        grev[b].push_back({a, c});
    }

    // bfs找最短路径
    vector<vector<int>> floor(n + 1, vector<int>());
    vector<int> dist(n + 1, INF); dist[1] = 0;
    queue<int> q; q.push(1);
    while(!q.empty()) {
        int u = q.front(); q.pop();
        for(auto [v, c] : g[u]) {
            if(dist[v] < INF) continue;
            dist[v] = dist[u] + 1;
            q.push(v);
            floor[dist[v]].push_back(v);
        }
    }

    int cnt = 1;
    vector<int> vis(n + 1, 0); vis[1] = 1;
    vector<ll> ans(n + 1);
    vector<int> rank(n + 1); rank[1] = 1;

    for(int d = 1; d <= n; d ++) {
        vector<tpi> val;
        for(int u : floor[d]) {
            int cr = -1, rare = 0;
            for(auto [v, c] : grev[u]) {
                if(dist[v] != dist[u] - 1) continue;
                if(cr == -1) cr = v, rare = c;
                else if(rank[v] < rank[cr]) cr = v, rare = c;
                else if(rank[v] == rank[cr] && c < rare) rare = c, cr = v;
            }
            val.push_back({rank[cr], rare, u});
            ans[u] = (ans[cr] * 10 + rare) % MOD;
            cnt ++;
        }
        sort(val.begin(), val.end());
        auto [r, e, u] = val[0];
        rank[u] = 1;
        for(int i = 1; i < val.size(); i ++) {
            auto [ru, ra, u] = val[i - 1];
            auto [rv, re, v] = val[i];
            if(ru == rv && ra == re) rank[v] = rank[u];
            else rank[v] = rank[u] + 1;
        }

        if(cnt == n) break;
    }
    for(int i = 2; i <= n; i ++) cout << ans[i] << "\n";
}

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