[2017浙工大之江学院决赛 H] qwb与学姐(并查集,按秩合并,最小生成树,LCA)

题目链接:http://115.231.222.240:8081/JudgeOnline/problem.php?cid=1005&pid=7

题意:中文题面。

手动画一下会发现所求边必然存在于最大生成树上,那么就可以首先构造一棵最大生成树。

问题转化成一棵树上求两个点之间的链上的最短边,用倍增lca就可以做了,但是我不会。

于是可以考虑建树时的操作,在求最大生成树的时候按秩合并,即集合大的根要做集合小的根的父亲,这样连一条有向边。因为最大声成树是从大到小遍历的,所以能保证点与点相连的链上的最短的那条边一直被更新着,并且能够维持整棵树高不会超过log(n)。

然后dfs预处理一下,查询的时候求lca,每次维护最短的边即可。

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 
  4 typedef long long LL;
  5 typedef struct Edge {
  6     int u, v, w, next;
  7 }Edge;
  8 const int maxn = 50500;
  9 const int maxm = 200200;
 10 int h1[maxn], h2[maxn], pre[maxn], rk[maxn];
 11 int n, m, q, ecnt1, ecnt2;
 12 int depth[maxn], dis[maxn];
 13 Edge e[maxm<<1], ee[maxn<<1];
 14 
 15 void init() {
 16     for(int i = 1; i <= n; i++) pre[i] = i;
 17     memset(h1, -1, sizeof(h1));
 18     memset(h2, -1, sizeof(h2));
 19     memset(rk, 0, sizeof(rk));
 20     memset(depth, 0, sizeof(depth));
 21     memset(dis, 0, sizeof(dis));
 22     ecnt1 = ecnt2 = 0;
 23 }
 24 
 25 void a1(int u, int v, int w) {
 26     e[ecnt1].u = u, e[ecnt1].v = v, e[ecnt1].w = w;
 27     e[ecnt1].next = h1[u], h1[u] = ecnt1++;
 28 }
 29 
 30 void a2(int u, int v, int w) {
 31     ee[ecnt2].u = u, ee[ecnt2].v = v, ee[ecnt2].w = w;
 32     ee[ecnt2].next = h2[u], h2[u] = ecnt2++;
 33 }
 34 
 35 bool cmp(Edge a, Edge b) { return a.w > b.w; }
 36 int find(int x) { return x == pre[x] ? x : pre[x] = find(pre[x]); }
 37 
 38 int unite(int x, int y, int w) {
 39     x = find(x); y = find(y);
 40     if(x != y) {
 41         if(rk[x] >= rk[y]) {
 42             if(rk[x] == rk[y]) rk[x]++;
 43             pre[y] = x, a2(x, y, w);
 44         }
 45         else pre[x] = y, a2(y, x, w);
 46         return 1;
 47     }
 48     return 0;
 49 }
 50 
 51 void dfs(int u, int p) {
 52     for(int i = h2[u]; ~i; i=ee[i].next) {
 53         int v = ee[i].v, w = ee[i].w;
 54         if(p == v) continue;
 55         depth[v] = depth[u] + 1;
 56         dis[v] = w; pre[v] = u;
 57         dfs(v, u);
 58     }
 59 }
 60 
 61 int query(int u, int v) {
 62     int ret = 0x7f7f7f7f;
 63     if(depth[u] < depth[v]) swap(u, v);
 64     while(depth[u] > depth[v]) {
 65         ret = min(ret, dis[u]);
 66         u = pre[u];
 67     }
 68     while(u != v) {
 69         ret = min(ret, min(dis[u], dis[v]));
 70         u = pre[u]; v = pre[v];
 71     }
 72     return ret;
 73 }
 74 
 75 int main() {
 76     // freopen("in", "r", stdin);
 77     int u, v, w;
 78     while(~scanf("%d%d%d",&n,&m,&q)) {
 79         init();
 80         for(int i = 0; i < m; i++) {
 81             scanf("%d%d%d",&u,&v,&w);
 82             a1(u, v, w); a1(v, u, w);
 83         }
 84         sort(e, e+ecnt1, cmp);
 85         int mst = 0;
 86         for(int i = 0; i < ecnt1; i++) {
 87             u = e[i].u; v = e[i].v; w = e[i].w;
 88             if(unite(u, v, w)) mst += w;
 89         }
 90         int rt = find(1);
 91         memset(pre, -1, sizeof(pre));
 92         depth[rt] = 1;
 93         dfs(rt, 0);
 94         while(q--) {
 95             scanf("%d%d",&u,&v);
 96             printf("%d\n", query(u, v));
 97         }
 98     }
 99     return 0;
100 }

 

posted @ 2017-06-02 22:00  Kirai  阅读(388)  评论(2编辑  收藏  举报