P9432 [NAPC-#1] rStage5 - Hard Conveyors
感谢此题让我知道了 Dijkstra
的一种新用法。
题意:
给定一棵 \(n\) 个节点的无根树以及树上的 \(k\) 个关键节点,给定边的长度。有 \(q\) 次询问,每次给出 \(s,t\),问从 \(s\) 到 \(t\) 且经过至少一个关键节点的路径的最短长度。
分析:
显然经过至少一个关键节点的条件可以分为两种情况判断:
-
如果从 \(s\) 到 \(t\) 的简单路径上已经有关键点,那么直接输出 \(s\) 到 \(t\) 的简单路径长度即可。这个过程显然可以以 \(1\) 为根处理出每个节点 \(u\) 到节点 \(1\) 的距离 \(len_u\),那么长度 \(ans\) 就是 \(len_u+len_v-2\times len_{lca(u,v)}\)。
-
如果从 \(s\) 到 \(v\) 的简单路径上没有关键点,那么显然找到最近的关键点 \(v\),走到 \(v\) 再回到简单路径上的方法可以得到最优解。而不妨预处理出每个点到任意一个关键点的最短距离 \(dis\) 数组,则路径到任意关键点的最短距离就可以定义为简单路径上 \(dis\) 数组的最小值 \(mindis\),由此答案只需要在 \(ans\) 的基础上再加上 \(2\times mindis\)。而多次查询路径上区间有关信息的问题显然可以用线段树 \(+\) 树链剖分实现,那么关键就是如何计算每个点到任意一个关键点的最短距离。
这时候就需要 Dijkstra
的经典(神奇逆天)多源最短路写法了:将每个关键点的初始 \(dis\) 均设为 \(0\),再跑普通的堆优化 Dijkstra
,此时 \(dis\) 数组存储的就是每个点到任意一个关键点的最短距离了。
核心代码如下:
priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > q;
inline void dij() {
memset(dis, 0x3f, sizeof dis);
memset(vis, 0, sizeof vis);
for (int i = 1; i <= k; i++) {
dis[p[i]] = 0;
q.push({0, p[i]});
}
while (!q.empty()) {
auto u = q.top().second;
q.pop();
if (vis[u]) continue;
vis[u] = 1;
for (auto i : G[u]) {
int v = i.first, w = i.second;
if (dis[v] > dis[u] + w) {
dis[v] = dis[u] + w;
q.push({dis[v], v});
}
}
}
}
剩下的就是经典的普通树剖维护 \(dis\) 数组的区间最小值了。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e5 + 5;
int n, m, k;
vector<pair<int, int> > G[maxn];
struct data {
int l, r, dat;
} t[maxn << 2];
int dep[maxn], fa[maxn], siz[maxn], son[maxn], top[maxn], dfn[maxn], rk[maxn], cnt;
int len[maxn];
int dis[maxn], p[maxn];
bool vis[maxn];
priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > q;
inline void dfs1(int u, int f) {
dep[u] = dep[f] + 1;
siz[u] = 1;
for (auto i : G[u]) {
int v = i.first, w = i.second;
if (v == f) continue;
fa[v] = u;
len[v] = len[u] + w;
dfs1(v, u);
siz[u] += siz[v];
if (siz[son[u]] < siz[v]) son[u] = v;
}
}
inline void dfs2(int u, int tp) {
top[u] = tp;
dfn[u] = ++cnt;
rk[cnt] = u;
if (!son[u]) return;
dfs2(son[u], tp);
for (auto i : G[u]) {
int v = i.first;
if (v == fa[u] or v == son[u]) continue;
dfs2(v, v);
}
}
inline void build(int p, int l, int r) {
t[p].l = l, t[p].r = r;
if (l == r) {
t[p].dat = dis[rk[l]];
return;
}
int mid = (l + r) >> 1;
build(p << 1, l, mid);
build(p << 1 | 1, mid + 1, r);
t[p].dat = min(t[p << 1].dat, t[p << 1 | 1].dat);
}
inline int query(int p, int l, int r) {
if (l <= t[p].l and t[p].r <= r) return t[p].dat;
int mid = (t[p].l + t[p].r) >> 1, res = 0x3f3f3f3f;
if (l <= mid) res = min(res, query(p << 1, l, r));
if (mid < r) res = min(res, query(p << 1 | 1, l, r));
return res;
}
inline void dij() {
memset(dis, 0x3f, sizeof dis);
memset(vis, 0, sizeof vis);
for (int i = 1; i <= k; i++) {
dis[p[i]] = 0;
q.push({0, p[i]});
}
while (!q.empty()) {
auto u = q.top().second;
q.pop();
if (vis[u]) continue;
vis[u] = 1;
for (auto i : G[u]) {
int v = i.first, w = i.second;
if (dis[v] > dis[u] + w) {
dis[v] = dis[u] + w;
q.push({dis[v], v});
}
}
}
}
inline int get(int u, int v) {
int res = 0x3f3f3f3f, ans = 0, uu = u, vv = v;
while (top[u] != top[v]) {
if (dep[top[u]] < dep[top[v]]) swap(u, v);
res = min(res, query(1, dfn[top[u]], dfn[u]));
u = fa[top[u]];
}
if (dep[u] > dep[v]) swap(u, v);
res = min(res, query(1, dfn[u], dfn[v]));
ans = len[uu] + len[vv] - len[u] * 2;
return ans + res * 2;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> m >> k;
for (int i = 1; i < n; i++) {
int u, v, w;
cin >> u >> v >> w;
G[u].push_back({v, w});
G[v].push_back({u, w});
}
for (int i = 1; i <= k; i++) cin >> p[i];
dij();
dfs1(1, 1);
dfs2(1, 1);
build(1, 1, n);
for (int i = 1; i <= m; i++) {
int u, v;
cin >> u >> v;
cout << get(u, v) << "\n";
}
}
标签:-#,int,res,top,Hard,P9432,len,关键点,dis
From: https://www.cnblogs.com/George-Pig-Orz/p/17733423.html