F. Cheap Robot(kruskal 重构树)

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] Tdis[u]

也就是说如果离 u u u最近的中心点不是终点,那么我们在 u u u点的能量可以更新为 C − d i s [ u ] C - dis[u] Cdis[u],且有 C − d i s [ u ] ≥ T C - dis[u] \geq T Cdis[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} Cdis[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] Cdis[u]Wu,vdis[v]也同样成立,也就是有 C ≥ d i s [ u ] + d i s [ v ] + w u , v C \geq dis[u] + dis[v] + w_{u, v} Cdis[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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值