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;
}

原文地址:https://www.cnblogs.com/Mrzdtz220/p/10797605.html

时间: 2024-08-28 09:11:34

HDU 2887 Watering Hole(MST + 倍增LCA)的相关文章

hdu 2586 How far away ?倍增LCA

hdu 2586 How far away ?倍增LCA 题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=2586 思路: 针对询问次数多的时候,采取倍增求取LCA,同时跟新距离数组 因为 \(2^{16} > 40000\) 所以所以表示祖先的数组dp[][]第二维取到16即可 就这道题来说,与比较tarjan比较,稍快一点 代码: #include <iostream> #include <algorithm> #includ

洛谷P4180 [Beijing2010组队]次小生成树Tree(最小生成树,LCT,主席树,倍增LCA,倍增,树链剖分)

洛谷题目传送门 %%%天平巨佬和山楠巨佬%%% 他们的题解 思路分析 具体思路都在两位巨佬的题解中.这题做法挺多的,我就不对每个都详细讲了,泛泛而谈吧. 首先kruskal把最小生成树弄出来,因为要求次小生成树.至于为什么次小一定只在最小的基础上改变了一条边,我也不会证......打表找规律大法好 剩下的可以有一堆数据结构来维护最大值和次大值(原理两位巨佬都讲清楚了,这里只分析一下算法的优劣) 倍增+LCA 山楠巨佬的做法,我也写了这一种.复杂度\(O(MlogM(kruscal)+MlogN(

【bzoj1146】[CTSC2008]网络管理Network 倍增LCA+dfs序+树状数组+主席树

题目描述 M公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门.为了让分布在世界各地的N个部门之间协同工作,公司搭建了一个连接整个公司的通信网络.该网络的结构由N个路由器和N-1条高速光缆组成.每个部门都有一个专属的路由器,部门局域网内的所有机器都联向这个路由器,然后再通过这个通信子网与其他部门进行通信联络.该网络结构保证网络中的任意两个路由器之间都存在一条直接或间接路径以进行通信. 高速光缆的数据传输速度非常快,以至于利用光缆传输的延迟时间可以忽略.但是由于路由器老化,在这些

【bzoj4242】水壶 BFS+最小生成树+倍增LCA

题目描述 JOI君所居住的IOI市以一年四季都十分炎热著称. IOI市是一个被分成纵H*横W块区域的长方形,每个区域都是建筑物.原野.墙壁之一.建筑物的区域有P个,编号为1...P. JOI君只能进入建筑物与原野,而且每次只能走到相邻的区域中,且不能移动到市外. JOI君因为各种各样的事情,必须在各个建筑物之间往返.虽然建筑物中的冷气设备非常好,但原野上的日光十分强烈,因此在原野上每走过一个区域都需要1单位的水.此外,原野上没有诸如自动售货机.饮水处之类的东西,因此IOI市的市民一般都携带水壶出

Codeforces 418d Big Problems for Organizers [树形dp][倍增lca]

题意: 给你一棵有n个节点的树,树的边权都是1. 有m次询问,每次询问输出树上所有节点离其较近结点距离的最大值. 思路: 1.首先是按照常规树形dp的思路维护一个子树节点中距离该点的最大值son_dis[i],维护非子树节点中距离该点的最大值fa_dis[i]; 2.对于每个节点维护它最大的三个儿子节点的son_dis; 3.维护up[i][j]和down[i][j]数组,这个类似倍增lca里边的fa[i][j],up[i][j]代表的含义是从第j个点向上到它的第2^i个父节点这条链上的点除了该

Tsinsen A1505. 树(张闻涛) 倍增LCA,可持久化线段树,DFS序

题目:http://www.tsinsen.com/A1505 A1505. 树(张闻涛) 时间限制:1.0s   内存限制:512.0MB 总提交次数:196   AC次数:65   平均分:58.62 将本题分享到: 查看未格式化的试题   提交   试题讨论 试题来源 2013中国国家集训队第二次作业 问题描述 给定一棵N个节点的树,每个点有一个权值,有M个询问(a,b,c)若a 为1,回答b到c路径上的最小权值,若a为2,回答b到c路径上的最大权值,若a为3,回答b到c路径上的所有权值的

BZOJ 2243: [SDOI2011]染色 树链剖分 倍增lca 线段树

2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/problem.php?id=2243 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“112221”由3段组成:“11”.“222”和“1”. 请你写

【BZOJ2815】【ZJOI2012】灾难 阿米巴和小强题 动态倍增LCA 灾难树

广告: #include <stdio.h> int main() { puts("转载请注明出处[vmurder]谢谢"); puts("网址:blog.csdn.net/vmurder/article/details/44104163"); } 题意: 原题面请见JSShining博客 http://www.cnblogs.com/JS-Shining/archive/2013/01/12/2857429.html 题解: 我们构建一颗灾难树,使得一

BZOJ 2791 Poi2012 Rendezvous 倍增LCA

题目大意:给定一棵内向森林,多次给定两个点a和b,求点对(x,y)满足: 1.从a出发走x步和从b出发走y步会到达同一个点 2.在1的基础上如果有多解,那么要求max(x,y)最小 3.在1和2的基础上如果有多解,那么要求min(x,y)最小 4.如果在1.2.3的基础上仍有多解,那么要求x>=y 因此那个x>=y是用来省掉SPJ的,不是题目要求- - 容易发现: 如果a和b不在同一棵内向树上,显然无解,否则一定有解 定义根为从一个点出发能走到的第一个环上点,如果a和b的根相同,则到达LCA是