bzoj 3732: Network 树上两点边权最值

http://www.lydsy.com/JudgeOnline/problem.php?id=3732

首先想到,要使得最长边最短,应该尽量走最短的边,在MST上。

然后像LCA那样倍增娶个最大值

#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false)
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL;
const int maxn = 30000 + 20;
struct Edge {
    int u, v, w, tonext;
    bool operator < (const struct Edge & rhs) const {
        return w < rhs.w;
    }
} e[maxn], t[maxn];
int first[maxn], num;
void addEdge(int u, int v, int w) {
    e[num].u = u, e[num].v = v, e[num].w = w, e[num].tonext = first[u];
    first[u] = num++;
}
int tfa[maxn];
int tofind(int u) {
    if (tfa[u] == u) return u;
    else return tfa[u] = tofind(tfa[u]);
}
const int need = 20;
int mx[maxn][25];
int ansc[maxn][25], deep[maxn], fa[maxn];  //所有只需初始值,不需要初始化。
void init_LCA(int cur) {   //1 << 20就有1048576(1e6)了。
    ansc[cur][0] = fa[cur]; //跳1步,那么祖先就是爸爸
    if (cur != 1) {
        for (int i = first[fa[cur]]; ~i; i = e[i].tonext) {
            int v = e[i].v;
            if (v == cur) {
                mx[cur][0] = e[i].w;
                break;
            }
        }
    }
    int haha = mx[cur][0];
    for (int i = 1; i <= need; ++i) { //倍增思路,递归处理
        ansc[cur][i] = ansc[ansc[cur][i - 1]][i - 1];
        mx[cur][i] = mx[ansc[cur][i - 1]][i - 1];
        mx[cur][i] = max(mx[cur][i], haha);
        haha = max(haha, mx[cur][i]);  //上到极限的时候需要取个路经的最大值。
    }
    for (int i = first[cur]; ~i; i = e[i].tonext) {
        int v = e[i].v;
        if (v == fa[cur]) continue;
        fa[v] = cur;
        deep[v] = deep[cur] + 1;
        init_LCA(v);
    }
}
int LCA(int x, int y) {
    int res = 0;
    if (deep[x] < deep[y]) swap(x, y); //需要x是最深的
    for (int i = need; i >= 0; --i) { //从大到小枚举,因为小的更灵活
        if (deep[ansc[x][i]] >= deep[y]) { //深度相同,走进去就对了。就是要去到相等。
            res = max(res, mx[x][i]);
            x = ansc[x][i];
        }
    }
    if (x == y) return res;
    for (int i = need; i >= 0; --i) {
        if (ansc[x][i] != ansc[y][i]) { //走到第一个不等的地方,
            res = max(res, mx[x][i]);
            res = max(res, mx[y][i]);
            x = ansc[x][i];
            y = ansc[y][i];
        }
    }
    res = max(res, mx[x][0]);
    res = max(res, mx[y][0]);
    return res; //再跳一步就是答案
}

void work() {
    num = 0;
    memset(first, -1, sizeof first);
    int n, m, k;
    scanf("%d%d%d", &n, &m, &k);
    for (int i = 1; i <= n; ++i) tfa[i] = i;
    for (int i = 1; i <= m; ++i) {
        scanf("%d%d%d", &t[i].u, &t[i].v, &t[i].w);
    }
    sort(t + 1, t + 1 + m);
    int sel = 0;
    for (int i = 1; i <= m; ++i) {
        if (sel == n - 1) break;
        int x = tofind(t[i].u), y = tofind(t[i].v);
        if (x == y) continue;
        sel++;
        tfa[y] = x;
        addEdge(t[i].u, t[i].v, t[i].w);
        addEdge(t[i].v, t[i].u, t[i].w);
    }
    fa[1] = 1, deep[1] = 0;
    init_LCA(1);
//    printf("%d\n", mx[2][1]);
    for (int i = 1; i <= k; ++i) {
        int u, v;
        scanf("%d%d", &u, &v);
        printf("%d\n", LCA(u, v));
    }
}

int main() {
#ifdef local
    freopen("data.txt", "r", stdin);
//    freopen("data.txt", "w", stdout);
#endif
    work();
    return 0;
}

时间: 2024-12-18 19:45:31

bzoj 3732: Network 树上两点边权最值的相关文章

BZOJ 3732 Network

2016.1.28 纪念我BZOJ第一题 Description 给你N个点的无向图 (1 <= N <= 15,000),记为:1…N. 图中有M条边 (1 <= M <= 30,000) ,第j条边的长度为: d_j ( 1 < = d_j < = 1,000,000,000). 现在有 K个询问 (1 < = K < = 15,000). 每个询问的格式是:A B,表示询问从A点走到B点的所有路径中,最长的边最小值是多少? Input 第一行: N,

BZOJ 3732 Network Link-Cut-Tree (我是认真的!!

题目大意:给定一个n个点m条边的无向连通图.k次询问两点之间全部路径中最长边的最小值 LCT的裸题! 首先维护一个动态的最小生成树,然后每次增加边时删除两点间路径上权值最大的边.最后询问时直接求x到y链上的最大权值就可以.水爆了! ! . .. 好吧开玩笑的 真正的题解见http://blog.csdn.net/popoqqq/article/details/39755703 我仅仅是闲得无聊水一发LCT罢了0.0 TLE了好久... 由于有边权为0的边我没更新... #include<cstd

[BZOJ 1146]网络管理Network 树上带修改路径k值

题目意思非常清楚,就是要求树上带修改的路径k大值 如果不带修改的话,我会用树上主席树去搞,以父子关系建树,可以参见 [BZOJ 3123]森林 但是带修改就不会打了QAQ,于是去学了另一种在dfs序上搞的方法(同时感谢呵呵酵母菌的帮助) 其实思想是一样的,就是搞出来节点到根路径的线段树,然后用x+y-lca-fa(lca)去差分出来树上路径的线段树,再去里面查询k值 那么我们怎么得到点到根路径的线段树呢?可以在dfs序上差分啊! dfs序列上的dfnl[x]~dfnr[x]包含的是以x为根节点的

BZOJ 3732 Network Kruskal+倍增LCA

题目大意:给定一个n个点m条边的无向连通图,k次询问两点之间所有路径中最长边的最小值 NOIP2013 货车运输,几乎就是原题...只不过最小边最大改成了最大边最小... 首先看到最大值最小第一反应二分答案 但是二分答案O(kmlogn)明显做不了 这里我们考虑最小生成树 先生成一棵最小生成树,然后每次询问利用倍增LCA求出路径上的最大权值即可 本蒟蒻居然把LCA写挂了... 而且样例还过了... 伤不起啊... 90%达成 剩下一道刷点啥呢... #include<cstdio> #incl

BZOJ 3732 Network Kruskal重构树

题目大意:给定一个n个点m条边的无向连通图,k次询问两点之间所有路径中最长边的最小值 Kruskal+倍增LCA做法见http://blog.csdn.net/popoqqq/article/details/39755703 LCT做法见http://blog.csdn.net/popoqqq/article/details/39929277 Kruskal重构树真是强大--一不小心手滑就RANK1啥的-- 每加入一条边时,我们并不链接这条边的两端点,而是把这条边两端点所在并查集的根连接起来,而

BZOJ 3732 Network 最小瓶颈路

题目大意:给出一个无向边,很多询问,问x,y两地之间的最长路最短是多少. 思路:乍一看好像是二分啊.的确这个题二分可以做,但是时间会慢很多,有的题直接就T掉(NOIP2013货车运输).其实这个题的模型就是最小瓶颈路模型.解法就是把无向图变成一个最小生成树,然后两点之间的最长路就是满足题意的答案. CODE: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm&g

3732: Network

3732: Network Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 395  Solved: 179[Submit][Status] Description 给你N个点的无向图 (1 <= N <= 15,000),记为:1…N. 图中有M条边 (1 <= M <= 30,000) ,第j条边的长度为: d_j ( 1 < = d_j < = 1,000,000,000). 现在有 K个询问 (1 < = K

POJ 1986 Distance Queries LCA树上两点的距离

题目来源:POJ 1986 Distance Queries 题意:给你一颗树 q次询问 每次询问你两点之间的距离 思路:对于2点 u v dis(u,v) = dis(root,u) + dis(root,v) - 2*dis(roor,LCA(u,v)) 求最近公共祖先和dis数组 #include <cstdio> #include <cstring> #include <vector> using namespace std; const int maxn =

F. Drivers Dissatisfaction+最小生成树+lca求树上两点的最大值

题目链接:F. Drivers Dissatisfaction 题意:n个点,m条边,每条边有一个w,代表这条路的不满意度,每一条边我们可以花费c来使不满意读-1:然后问你有s,找到一棵生成树是满意度最小 题解:对于s,我们可以知道花费在c最小的边上价值最优,我们可以先求一颗最小生成树,然后枚举没有用到的边,把连接这两点的最长边去掉判段能否更新最小值 这里求树上两点的最短路经过的最大值,我们可以有lca,或者树链抛分都可以 #include<bits/stdc++.h> #include<