CF196E Opening Portals

给定一个有 n n n个节点, m m m条边的无向联通图,有 k k k个点有 p o r t a l s portals portals,当经过了某个点,如果这个点有 p o r t a l portal portal,它就会永久开启,

对于任意两个开启的 p o r t a l portal portal,我们可以不需要花时间穿行,问开启所有的 p o r t a l portal portal需要多少时间。

可以考虑这是一个 1 − > S v 1->S_v 1>Sv S v S_v Sv是另 k k k p o r t a l s portals portals联通的最小生成树,

因此我们的答案就是 1 − > n e a r e s t ∈ k 1->nearest\in k 1>nearestk,再加上最小生成树的代价,

我们另这 k k k个点去跑最短路,然后把原本的边 { u , v , w } \{u, v, w\} {u,v,w}变为 { n e a r e s t ∈ k   o f   u , n e a r e s t ∈ k   o f   v , d i s [ u ] + d i s [ v ] + w } \{nearest \in k\ of\ u, nearest \in k\ of\ v, dis[u] + dis[v] + w\} {nearestk of u,nearestk of v,dis[u]+dis[v]+w}

k k k点中与 u u u最近的点, k k k点中与 v v v最近的点,边权同时更新,最后只需要跑一个最小生成树计算代价即可。

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

struct Res {
  int u, v;

  long long 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; 
  
  long long w;

  bool operator < (const Node &t) const {
    return w > t.w;
  }
};

int n, m, k, vis[N], fa[N], pre[N];

long long dis[N];

vector< pair<int, int> > G[N];

int find(int rt) {
  return rt == fa[rt] ? rt : fa[rt] = find(fa[rt]);
}

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", &n, &m);
  for (int i = 1; i <= m; i++) {
    edge[i].read();
    G[edge[i].u].push_back({edge[i].v, edge[i].w});
    G[edge[i].v].push_back({edge[i].u, edge[i].w});
  }
  scanf("%d", &k);
  priority_queue<Node> q;
  memset(dis, 0x3f, sizeof dis);
  for (int i = 1, x; i <= k; i++) {
    scanf("%d", &x);
    q.push({x, 0});
    pre[x] = x;
    dis[x] = 0;
  }
  while (q.size()) {
    auto u = q.top().u;
    q.pop();
    if (vis[u]) {
      continue;
    }
    vis[u] = 1;
    for (auto &to : G[u]) {
      if (dis[to.first] > dis[u] + to.second) {
        dis[to.first] = dis[u] + to.second;
        pre[to.first] = pre[u];
        q.push({to.first, dis[to.first]});
      }
    }
  }

  long long ans = dis[1];

  for (int i = 1; i <= m; i++) {
    edge[i].w += dis[edge[i].u] + dis[edge[i].v];
    edge[i].u = pre[edge[i].u], edge[i].v = pre[edge[i].v];
  }
  sort(edge + 1, edge + 1 + m);
  for (int i = 1; i <= n; i++) {
    fa[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++;
      fa[u] = v;
      ans += edge[i].w;
    }
  }
  printf("%lld\n", ans);
  return 0;
}