UVA 11354 - Bond (最小生成树 + 树链剖分)

题目链接~~>

做题感悟:这题开始看到时感觉不是树不好处理,一想可以用 Kruskal 处理成树 ,然后就好解决了。

解题思路:

先用 Kruskal 处理出最小生成树,然后用树链剖分 + 线段树处理就可以了。

代码:

#include<iostream>
#include<sstream>
#include<map>
#include<cmath>
#include<fstream>
#include<queue>
#include<vector>
#include<sstream>
#include<cstring>
#include<cstdio>
#include<stack>
#include<bitset>
#include<ctime>
#include<string>
#include<cctype>
#include<iomanip>
#include<algorithm>
using namespace std  ;
#define INT long long int
#define L(x)  (x * 2)
#define R(x)  (x * 2 + 1)
const int INF = 0x3f3f3f3f ;
const double esp = 0.0000000001 ;
const double PI = acos(-1.0) ;
const int mod = 1000000007 ;
const int MY = (1<<5) + 5 ;
const int MX = 100000 + 5 ;
int n ,m ,nx ,idx ,num ;
int head[MX] ,ti[MX] ,siz[MX] ,son[MX] ,father[MX] ,top[MX] ,dep[MX] ;
struct NODE
{
    int u ,v ,w ;
}e[MX] ;
struct MNODE
{
    int u ,v ,w ;
}t[MX] ;
struct Edge
{
    int u ,v ,w ,next ;
}E[MX*2] ;
void addedge(int u ,int v ,int w)
{
    E[num].v = v ; E[num].w = w ; E[num].next = head[u] ; head[u] = num++ ;
    E[num].v = u ; E[num].w = w ; E[num].next = head[v] ; head[v] = num++ ;
}
bool cmp(NODE a ,NODE b)
{
    return a.w < b.w ;
}
int find(int u)
{
    if(father[u] != u)
       return find(father[u]) ;
    else   return u ;
}
void Kruskal()
{
    nx = 1 ;
    int u ,v ,fa ,fb ;
    sort(e ,e+m ,cmp) ;
    for(int i = 0 ;i <= n ; ++i)
        father[i] = i ;
    for(int i = 0 ;i < m ; ++i)
    {
        u = e[i].u ; v = e[i].v ;
        fa = find(u) ;
        fb = find(v) ;
        if(fa != fb)
        {
            father[fa] = fb ;
            addedge(u ,v ,e[i].w) ;
            t[nx].u = u ; t[nx].v = v ; t[nx++].w = e[i].w ;
        }
    }
}
void dfs_find(int u ,int fa)
{
    dep[u] = dep[fa] + 1 ;
    father[u] = fa ;
    siz[u] = 1 ;
    son[u] = 0 ;
    for(int i = head[u] ;i != -1 ;i = E[i].next)
    {
        int v = E[i].v ;
        if(v == fa)  continue ;
        dfs_find(v ,u) ;
        siz[u] += siz[v] ;
        if(siz[son[u]] < siz[v])  son[u] = v ;
    }
}
void dfs_time(int u ,int fa)
{
    top[u] = fa ;
    ti[u] = idx++ ;
    if(son[u])   dfs_time(son[u] ,top[u]) ;
    for(int i = head[u] ;i != -1 ;i = E[i].next)
    {
        int v = E[i].v ;
        if(v == father[u] || v == son[u])  continue ;
        dfs_time(v ,v) ;
    }
}
struct node
{
    int le ,rt ,c ;
}T[MX*4] ;
void build(int i ,int le ,int rt)
{
    T[i].le = le ; T[i].rt = rt ;
    T[i].c = 0 ;
    if(le == rt)  return ;
    int Mid = (le + rt)>>1 ;
    build(L(i) ,le ,Mid) ;
    build(R(i) ,Mid+1 ,rt) ;
}
void update(int i ,int pos ,int w)
{
    if(T[i].le == T[i].rt)
    {
        T[i].c = w ;
        return ;
    }
    int Mid = (T[i].le + T[i].rt)>>1 ;
    if(pos <= Mid)   update(L(i) ,pos ,w) ;
    else     update(R(i) ,pos ,w) ;
    T[i].c = max(T[L(i)].c ,T[R(i)].c) ;
}
int section(int i ,int le ,int rt)
{
    if(T[i].le == le && T[i].rt == rt)
        return T[i].c ;
    int Mid = (T[i].le + T[i].rt)>>1 ;
    if(le > Mid)   return section(R(i) ,le ,rt) ;
    else if(rt <= Mid)  return section(L(i) ,le ,rt) ;
    else
          return max(section(L(i) ,le ,Mid) ,section(R(i) ,Mid+1 ,rt)) ;
}
int LCA(int u ,int v)
{
    int ans = 0 ;
    while(top[u] != top[v])
    {
        if(dep[top[u]] < dep[top[v]])
             swap(u ,v) ;
        ans = max(ans ,section(1 ,ti[top[u]] ,ti[u])) ;
        u = father[top[u]] ;
    }
    if(dep[u] > dep[v])  swap(u ,v) ;
    if(u != v)
        ans = max(ans ,section(1 ,ti[u]+1 ,ti[v])) ;
    return ans ;
}
int main()
{
    int u ,v ,Q ;
    bool first = false ;
    while(~scanf("%d%d" ,&n ,&m))
    {
        if(first) puts("") ;
        first = true ;
        num = 0 ;
        memset(head ,-1 ,sizeof(head)) ;
        for(int i = 0 ;i < m ; ++i)
           scanf("%d%d%d" ,&e[i].u ,&e[i].v ,&e[i].w) ;
        Kruskal() ;
        dep[1] = siz[0] = 0 ;
        dfs_find(1 ,1) ;
        idx = 1 ;
        dfs_time(1 ,1) ;
        build(1 ,1 ,n) ;
        for(int i = 1 ;i < nx ; ++i)
        {
            if(dep[t[i].u] < dep[t[i].v])
                swap(t[i].u ,t[i].v) ;
            update(1 ,ti[t[i].u] ,t[i].w) ;
        }
        scanf("%d" ,&Q) ;
        for(int i = 0 ;i < Q ; ++i)
        {
            scanf("%d%d" ,&u ,&v) ;
            printf("%d\n" ,LCA(u ,v)) ;
        }
    }
    return 0 ;
}
时间: 2024-10-28 14:24:33

UVA 11354 - Bond (最小生成树 + 树链剖分)的相关文章

UVA 11354 - Bond(树链剖分)

UVA 11354 - Bond 题目链接 题意:给定一个图,要求每次询问两点,求出这两点间路径最大危险系数最小 思路:先求最小生成树,在生成树上每次询问求LCT就可以了,利用树链剖分求解 代码: #include <cstdio> #include <cstring> #include <algorithm> #include <vector> using namespace std; #define lson(x) ((x<<1)+1) #d

UVa 11354 Bond 最小生成树+LCA倍增

题目来源:UVa 11354 Bond 题意:n个点m条边的图 q次询问 找到一条从s到t的一条边 使所有边的最大危险系数最小 思路:使最大的危险系数尽量小 答案是最小生成树上的边 然后用LCA倍增法记录s和t到他们最近公共祖先的最大值 #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn = 50010; const int INF =

uva 11354 bond 最小生成树

n个城市通过m条无向边连接,回答q个询问,每个询问形式为s,t,要找到一条s到t的路使得这条路上的最大危险系数最小. 还是最小瓶颈路,可是要快速回答每次询问,先求出最小生成树,转化为有根树,即找到s到t的路径上的最大边,在这一过程中倍增查找. 预处理的复杂度为nlogn,每次查询为logn. #include <cstdio> #include <cstring> #include <vector> #include <algorithm> using na

【bzoj2238】Mst 最小生成树+树链剖分+线段树

题目描述 给出一个N个点M条边的无向带权图,以及Q个询问,每次询问在图中删掉一条边后图的最小生成树.(各询问间独立,每次询问不对之后的询问产生影响,即被删掉的边在下一条询问中依然存在) 输入 第一行两个正整数N,M(N<=50000,M<=100000)表示原图的顶点数和边数. 下面M行,每行三个整数X,Y,W描述了图的一条边(X,Y),其边权为W(W<=10000).保证两点之间至多只有一条边. 接着一行一个正整数Q,表示询问数.(1<=Q<=100000) 下面Q行,每行

UVA 11354 Bond(最小生成树+lca+倍增求祖先节点)

题意:n个点m条边,每条边有一个权值,有q个询问,每次询问两点间的一条路径,使得这条路径上权值最大的边最小. 思路:很容易想到最小瓶颈路,但是查询太多,会超时,可以预处理出最小生成树,则问题转化为一棵树上的两点间路径中权值最大的那条边,设这两点为u,v,可以得到dist(u,v)=max(dist(u,lca(u,v)),dist(v,lca(v,lca))),其中lca(u,v)表示u和v的最近公共祖先,用倍增的思想预处理出每个结点的2^i的祖先fa[u][i],然后在离线求出lca后顺便计算

uva 11354 - Bond(树链剖分)

题目链接:uva 11354 - Bond 题目大意:给定一张图,每次询问两个节点路径上进过边的危险值的最大值的最小值. 解题思路:首先建立最小生成数,然后根据这棵树做树链剖分. #include <cstdio> #include <cstring> #include <vector> #include <algorithm> using namespace std; const int maxn = 50005; const int INF = 0x3f

[HDU 3710] Battle over Cities 树链剖分 最小生成树

题意 给定一张 $N$ 个点 $M$ 条边的无向连通图, 求删除任意点的最小生成树的边权之和. $0 < N \le 20000$ . $0 \le M \le 100000$ . 分析 我们会求整张图的最小生成树. 尝试求出来, 然后与我们所求进行联系. 我们发现, 把当前点 $x$ 断开之后, 会形成若干个连通块. 要求将这么多块连通的最小边权之和 $S$ , 答案即为 $原本答案 - 与 x 相连的边权之和 + S$ . 我们考虑对新图进行 Kruskal. 在此之前, 我们要尝试用具体的

jzoj5987. 【WC2019模拟2019.1.4】仙人掌毒题 (树链剖分+概率期望+容斥)

题面 题解 又一道全场切的题目我连题目都没看懂--细节真多-- 先考虑怎么维护仙人掌.在线可以用LCT,或者像我代码里先离线,并按时间求出一棵最小生成树(或者一个森林),然后树链剖分.如果一条边不是生成树上的边,它肯定会和树上\(u,v\)这条路径构成一个环,然后对于每条树边记录一下这条树边被覆盖过没有.如果\(u,v\)路径上有任何一条树边被覆盖过,那么就说明路径上有一条边已经在一个简单环中,这条非树边就不能加.否则就加上这条边并让这条路径上所有树边的覆盖次数加一 然后考虑期望连通块个数.首先

训练指南 UVA - 11354(最小生成树 + 倍增LCA)

layout: post title: 训练指南 UVA - 11354(最小生成树 + 倍增LCA) author: "luowentaoaa" catalog: true mathjax: true tags: - 最小生成树 - LCA - 图论 - 训练指南 Bond UVA - 11354 题意 给你一张无向图,然后有若干组询问,让你输出a->b的最小瓶颈路 题解 先求出最小生成树,然后对这个最小生成树做LCA. #include<bits/stdc++.h>