F. Cheap Robot
给定一个无向连通图,每条边有边权,路过需要消耗对应的电量(边权),有 k k k个中心点,
问从 a − > b a-> b a−>b,我们最少需要带多少电,设最小为 C C C,当通过一条边时,我们的电会减少(当电不够时即不能通过),当经过中心点时我们的电会立刻变为 C C C,
比较简单的想法:处理出任意两个中心点对的最短路出来,最后我们只需要得到中心点对的升序 k r u s k a l kruskal kruskal最小生成树,然后每次查询两点之间的 l c a lca lca的权值即可。
我们假设 d i s [ x ] dis[x] dis[x]为从 x x x到达最近的中心点所需要的电量,
设我们以 T T T电量到达了 u u u点,那么我们要能够到达终点(注意终点是一个中心点),那么一定有 T ≥ d i s [ u ] T \geq dis[u] T≥dis[u],
也就是说如果离 u u u最近的中心点不是终点,那么我们在 u u u点的能量可以更新为 C − d i s [ u ] C - dis[u] C−dis[u],且有 C − d i s [ u ] ≥ T C - dis[u] \geq T C−dis[u]≥T,
如果与 u u u直接相连的边为 v v v,要能够到达 v v v,那么一定有 C − d i s [ u ] ≥ W u , v C - dis[u] \geq W_{u, v} C−dis[u]≥Wu,v, W u , v W_{u, v} Wu,v是边权,
同时要使能从 v v v到达终点,那么 C − d i s [ u ] − W u , v ≥ d i s [ v ] C - dis[u] - W_{u, v} \geq dis[v] C−dis[u]−Wu,v≥dis[v]也同样成立,也就是有 C ≥ d i s [ u ] + d i s [ v ] + w u , v C \geq dis[u] + dis[v] + w_{u, v} C≥dis[u]+dis[v]+wu,v,
满足上面的式子,我们才可能通过一条边,然后从某条边走向终点,
所以我们可以把原本的一条边的边权转化为 W u , v = W u , v + d i s [ u ] + d i s [ v ] W_{u, v} = W_{u, v} + dis[u] + dis[v] Wu,v=Wu,v+dis[u]+dis[v],跑一个升序 k r u s k a l kruskal kruskal最小生成树,找 l c a lca lca权值即可
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
int n, nn, k, m, q, ff[N];
ll value[N], dis[N];
int head[N], vis[N], nex[N], to[N], cnt = 1;
int fa[N], dep[N], sz[N], top[N], son[N], id[N], tot;
struct Res {
int u, v;
ll w;
void read() {
scanf("%d %d %lld", &u, &v, &w);
}
bool operator < (const Res &t) const {
return w < t.w;
}
}edge[N];
struct Node {
int u;
ll w;
bool operator < (const Node &t) const {
return w > t.w;
}
};
vector<Node> G[N];
bool cmp(int x, int y) {
return id[x] < id[y];
}
int find(int rt) {
return ff[rt] == rt ? rt : ff[rt] = find(ff[rt]);
}
void add(int x, int y) {
to[cnt] = y;
nex[cnt] = head[x];
head[x] = cnt++;
}
void dfs1(int rt, int f) {
fa[rt] = f, dep[rt] = dep[f] + 1, sz[rt] = 1, id[rt] = ++tot;
for (int i = head[rt]; i; i = nex[i]) {
if (to[i] == f) {
continue;
}
dfs1(to[i], rt);
sz[rt] += sz[to[i]];
if (sz[son[rt]] < sz[to[i]]) {
son[rt] = to[i];
}
}
}
void dfs2(int rt, int tp) {
top[rt] = tp;
if (!son[rt]) {
return ;
}
dfs2(son[rt], tp);
for (int i = head[rt]; i; i = nex[i]) {
if (to[i] == fa[rt] || to[i] == son[rt]) {
continue;
}
dfs2(to[i], to[i]);
}
}
int lca(int x, int y) {
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) {
swap(x, y);
}
x = fa[top[x]];
}
return dep[x] < dep[y] ? x : y;
}
void kruskal() {
sort(edge + 1, edge + 1 + m);
for (int i = 1; i < N; i++) {
ff[i] = i;
}
for (int i = 1, cur = 1; i <= m && cur < n; i++) {
int u = find(edge[i].u), v = find(edge[i].v);
if (u ^ v) {
cur++, nn++;
ff[u] = ff[v] = nn;
value[nn] = edge[i].w;
add(nn, u), add(nn, v);
}
}
dfs1(nn, 0), dfs2(nn, nn);
}
void Dijkstra() {
priority_queue<Node> q;
memset(vis, 0, sizeof vis);
memset(dis, 0x3f, sizeof dis);
for (int i = 1; i <= k; i++) {
q.push({i, 0});
dis[i] = 0;
}
while (q.size()) {
int u = q.top().u;
q.pop();
if (vis[u]) {
continue;
}
vis[u] = 1;
for (auto to : G[u]) {
if (dis[to.u] > dis[u] + to.w) {
dis[to.u] = dis[u] + to.w;
q.push({to.u, dis[to.u]});
}
}
}
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
// ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
scanf("%d %d %d %d", &n, &m, &k, &q);
nn = n;
for (int i = 1; i <= m; i++) {
edge[i].read();
G[edge[i].v].push_back({edge[i].u, edge[i].w});
G[edge[i].u].push_back({edge[i].v, edge[i].w});
}
Dijkstra();
for (int i = 1; i <= m; i++) {
edge[i].w += dis[edge[i].u] + dis[edge[i].v];
}
kruskal();
while (q--) {
int u, v;
scanf("%d %d", &u, &v);
printf("%lld\n", value[lca(u, v)]);
}
return 0;
}