题面
题解
这是好久之前菊开讲的一道题目了。
可以发现在这道题目中,边比点更加重要,所以我们化边为点,将边权改为点权,边与边之间的边权就是题目所给的Trie树上LCA深度的和。
想到一个平方的暴力,每条边和它连向的点的出边连一条边。下一步考虑怎么优化。
对于每一个点,将它的入边和出边都拿出来,按照dfs序排序,那么可以考虑将每个点拆成入点和出点,因为\(\operatorname{lcp}(i, j)\)的值是\(i \to j\)之间所有的边的\(\operatorname{lcp}\)的最小值,于是前后缀优化连边就可以了。
可以考虑使用下面这个图来帮助理解:
说起来容易做起来难,代码留给读者作为练习
代码
#include <cstdio> #include <cstring> #include <algorithm> #include <queue> #define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout) inline int read() { int data = 0, w = 1; char ch = getchar(); while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar(); if (ch == '-') w = -1, ch = getchar(); while (ch >= '0' && ch <= '9') data = data * 10 + (ch ^ 48), ch = getchar(); return data * w; } const int N(50010); struct edge { int to, dis; }; int T, n, m, K, val[N * 20], pos[N], dis[N * 20], vis[N * 20], cur; std::vector<int> in[N], out[N]; std::vector<edge> G[N * 20]; namespace Tree { int fa[N], size[N], heavy[N], dfn[N], bel[N], dep[N], cnt; std::vector<int> T[N]; inline void Add(int x, int y) { T[x].push_back(y); } void dfs(int x) { size[x] = 1, heavy[x] = 0; for (auto i : T[x]) { fa[i] = x, dep[i] = dep[x] + 1, dfs(i), size[x] += size[i]; if (size[heavy[x]] < size[i]) heavy[x] = i; } } void dfs(int x, int chain) { dfn[x] = ++cnt, bel[x] = chain; if (heavy[x]) dfs(heavy[x], chain); for (auto i : T[x]) if (i != heavy[x]) dfs(i, i); } int LCA(int x, int y) { for (; bel[x] != bel[y]; x = fa[bel[x]]) if (dfn[bel[x]] < dfn[bel[y]]) std::swap(x, y); return dfn[x] < dfn[y] ? dep[x] : dep[y]; } } void Dijkstra() { std::priority_queue<std::pair<int, int>, std::vector<std::pair<int, int> >, std::greater<std::pair<int, int> > > Q; memset(dis, 127, (cur + 1) << 2), memset(vis, 0, sizeof vis); for (auto i : out[1]) Q.push(std::make_pair(dis[i] = val[i], i)); while (!Q.empty()) { int x = Q.top().second; Q.pop(); if (vis[x]) continue; vis[x] = 1; for (auto i : G[x]) if (dis[x] + val[i.to] + i.dis < dis[i.to]) Q.push(std::make_pair(dis[i.to] = dis[x] + val[i.to] + i.dis, i.to)); } } int Abs(int x) { return x < 0 ? -x : x; } int cmp(int x, int y) { return Tree::dfn[pos[Abs(x)]] < Tree::dfn[pos[Abs(y)]]; } void Link(int x) { static int stk[N], p1[N], p2[N], q1[N], q2[N]; int top = 0; for (auto i : in[x]) stk[++top] = i; for (auto i : out[x]) stk[++top] = -i; std::sort(stk + 1, stk + top + 1, cmp); for (int i = 1; i <= top; i++) p1[i] = ++cur, p2[i] = ++cur, q1[i] = ++cur, q2[i] = ++cur; for (int i = 2; i <= top; i++) G[p1[i - 1]].push_back((edge) {p1[i], 0}), G[q1[i - 1]].push_back((edge) {q1[i], 0}), G[p2[i]].push_back((edge) {p2[i - 1], 0}), G[q2[i]].push_back((edge) {q2[i - 1], 0}); for (int i = 1; i <= top; i++) if (stk[i] > 0) G[stk[i]].push_back((edge) {p1[i], 0}), G[stk[i]].push_back((edge) {p2[i], 0}); else stk[i] = -stk[i], G[q1[i]].push_back((edge) {stk[i], 0}), G[q2[i]].push_back((edge) {stk[i], 0}); for (int i = 1, x; i < top; i++) x = Tree::LCA(pos[stk[i]], pos[stk[i + 1]]), G[p1[i]].push_back((edge) {q1[i + 1], x}), G[p2[i + 1]].push_back((edge) {q2[i], x}); } int main() { #ifndef ONLINE_JUDGE file(cpp); #endif for (T = read(); T--; ) { n = read(), m = cur = read(), K = read(); memset(val, 0, sizeof val), Tree::cnt = 0; for (int i = 1, x, y; i <= m; i++) x = read(), y = read(), val[i] = read(), pos[i] = read(), out[x].push_back(i), in[y].push_back(i); for (int i = 1, x, y; i < K; i++) x = read(), y = read(), read(), Tree::Add(x, y); Tree::dfs(1), Tree::dfs(1, 1); for (int i = 2; i <= n; i++) Link(i); Dijkstra(); for (int i = 2; i <= n; i++) { int ans = 2e9; for (auto x : in[i]) ans = std::min(ans, dis[x]); printf("%d\n", ans); } for (int i = 1; i <= cur; i++) G[i].clear(); for (int i = 1; i <= n; i++) in[i].clear(), out[i].clear(); for (int i = 1; i <= K; i++) Tree::T[i].clear(); } return 0; }