HDU 2887 Watering Hole(MST + 倍增LCA)

 

传送门

总算是做上一道LCA的应用题了...

题意:有$n$个牧场, $m$根管道分别连接编号为$u,v$的牧场花费$p_{i}$,在第$i$个牧场挖口井需要花费$w_{i}$,有$P$根管道直接连通着$u,v$,即免费连上$u,v$

对每根免费管道输出让所有牧场都有水的最小花费

先是最小生成树,用0去连每一个点,边权就是每个点的权值,然后正常建,跑一遍最小生成树,把用到的边重新建一次图,然后就对每次询问的$u,v$,减掉他们之间的路径的最长边就是答案了

因为删去这其中一条边,再免费连上$u,v$,最后还是一棵树,最小花费就减去最长边就好了。

然后求两点路径上的最长边就得用到倍增LCA,本来有点想不太明白,然后画了个图就清楚了,再预处理的dfs中,求每个点的LCA就可以直接求最长边了

$cost[u][i]$表示u往上走$2^{i}$步之间的最长边

转移就是$cost[u][i]=\max (cost[u][i-1], cost[lca[u][i-1]][i-1])$
求出来的是$u$往上走1, 2, 4, ...步的距离

如果需要往上走3步的 答案即为$max(cost[u][0], cost[lca[u][0]][1])$

这在查询过程中实现即可

#include <bits/stdc++.h>
using namespace std;

inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); }
    return x * f;
}

const int N = 5e3 + 10;
const int M = 3e5 + 10;
struct Edge1 { 
    int u, v, c;
    bool operator < (const Edge1 &rhs) const {
        return c < rhs.c;
    } 
} e[N + M];
struct Edge {int v, next, c;} edge[2*N];
int cnt, head[N], fa[N], lca[N][20], cost[N][20], dep[N], n, m, q;
bool vis[N];
inline void addedge(int u, int v, int c) {
    edge[cnt].v = v; edge[cnt].c = c; edge[cnt].next = head[u]; head[u] = cnt++;
} 
void init() { 
    cnt = 0;
    for (int i = 0; i <= n; i++) fa[i] = i, head[i] = -1, dep[i] = 0, vis[i] = false;
    memset(cost, 0, sizeof(cost));
    memset(lca, 0, sizeof(lca));
}
int getfa(int x) { return x == fa[x] ? x : fa[x] = getfa(fa[x]); }

void dfs(int u) {
    lca[u][0] = fa[u];
    vis[u] = 1;
    for (int i = 1; i <= 16; i++) {
        lca[u][i] = lca[lca[u][i-1]][i-1];
        cost[u][i] = max(cost[u][i-1], cost[lca[u][i-1]][i-1]);
    }
    for (int i = head[u]; ~i; i = edge[i].next) {
        int v = edge[i].v, c = edge[i].c;
        if (vis[v]) continue;
        dep[v] = dep[u] + 1;
        cost[v][0] = c;
        fa[v] = u;
        dfs(v);
    }
}

int Lca(int u, int v) {
    if (dep[u] < dep[v]) swap(u, v);
    int ans = 0;
    int f = dep[u] - dep[v];
    for (int i = 0; i <= 16; i++) {
        if (f & (1 << i)) {
            ans = max(ans, cost[u][i]);
            u = lca[u][i];  
        }
    }
    if (u == v) return ans;
    for (int i = 16; i >= 0; i--) {
        if (lca[u][i] != lca[v][i]) {
            ans = max(ans, cost[u][i]);
            ans = max(ans, cost[v][i]);
            u = lca[u][i];
            v = lca[v][i];
        }
    }
    return max(max(ans, cost[u][0]), cost[v][0]);
}

int main() {
    while (~scanf("%d%d%d", &n, &m, &q)) {
        init();
        int sum = 0;
        int tol = 0;
        for (int i = 1; i <= n; i++) {
            int p = read();
            e[++tol].u = 0; e[tol].v = i; e[tol].c = p;
        }
        for (int i = 1; i <= m; i++) {
            int u = read(), v = read(), c = read();
            e[++tol].u = u, e[tol].v = v, e[tol].c = c;
        }
        sort(e + 1, e + 1 + tol);
        for (int i = 1; i <= tol; i++) {
            int u = getfa(e[i].u), v = getfa(e[i].v);
            if (u == v) continue;
            fa[v] = u;
            sum += e[i].c;
            addedge(e[i].u, e[i].v, e[i].c);
            addedge(e[i].v, e[i].u, e[i].c);
        }
        fa[0] = 0;
        dfs(0);
        while (q--) {
            int u = read(), v = read();
            printf("%d\n", sum - Lca(u, v));
        }
    }
    return 0;     
}
View Code

 

转载于:https://www.cnblogs.com/Mrzdtz220/p/10797605.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值