题目描述

给你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 < = 20,000)。 
每个询问的格式是:A B,表示询问从A点走到B点的所有路径中,最长的边最小值是多少?

输入

第一行: N, M, K。 
第2..M+1行: 三个正整数:X, Y, and D (1 <= X <=N; 1 <= Y <= N). 表示X与Y之间有一条长度为D的边。 
第M+2..M+K+1行: 每行两个整数A B,表示询问从A点走到B点的所有路径中,最长的边最小值是多少?

输出

对每个询问,输出最长的边最小值是多少。

样例输入

6 6 8
1 2 5
2 3 4
3 4 3
1 4 8
2 5 7
4 6 2
1 2
1 3
1 4
2 3
2 4
5 1
6 2
6 1

样例输出

5
5
5
4
4
7
4
5

提示

1 <= N <= 15,000

1 <= M <= 30,000

1 <= d_j <= 1,000,000,000

1 <= K <= 15,000

  首先根据最小生成树的性质可以知道两点间路径上最长边最小的那条路径,就是最小生成树上两点间的路径——因为最小生成树是按边权从小到大加边,所以保证两点间路径上的边都尽可能的小。那么只要先把最小生成树求出来,其他的边就没有用了。对于最小生成树求解,一种做法是倍增维护g[i][j]表示以i节点向上跳2j步路径上的边权最大值。今天来介绍另一种做法——kruskal重构树。什么是kruskal重构树呢?就是把最小生成树上的边从小到大,对于每一条边a-b边权为c,就把重构树上a所在子树的根节点和b所在子树的根节点(可以是自己)分别连到一个新建节点上,将那个点作为这两个点的父节点,而新建那个点的点权就是c。最后形成的一棵二叉树就是kruskal重构树。kruskal重构树是一个大根堆且两叶子节点的lca点权就是这两点在最小生成树上路径上的最大边权。这样对于每次询问只要找到两点lca就行了。

最小生成树+倍增

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
int n,m;
int next[100050];
int to[100050];
int val[100050];
int head[15010];
int f[15010][20];
int g[15010][20];
int d[15010];
int F[15010];
int tot=0;
int sum;
int num;
struct node
{
int x;
int y;
int v;
}a[50001];
void add(int x,int y,int v)
{
tot++;
next[tot]=head[x];
head[x]=tot;
to[tot]=y;
val[tot]=v;
}
int cmp(node a,node b)
{
return a.v<b.v;
}
int find(int x)
{
if(F[x]==x)
{
return x;
}
return F[x]=find(F[x]);
}
void dfs(int x,int fx)
{
f[x][0]=fx;
d[x]=d[fx]+1;
for(int j=1;(1<<j)<=n;j++)
{
f[x][j]=f[f[x][j-1]][j-1];
g[x][j]=max(g[f[x][j-1]][j-1],g[x][j-1]);
}
for(int i=head[x];i;i=next[i])
{
if(to[i]!=fx)
{
g[to[i]][0]=val[i];
dfs(to[i],x);
}
}
}
int lca(int x,int y)
{
int total=0;
if(d[x]<d[y])
{
swap(x,y);
}
int deep=d[x]-d[y];
for(int i=0;i<=18;i++)
{
if((deep&(1<<i))!=0)
{
total=max(total,g[x][i]);
x=f[x][i];
}
}
if(x==y)
{
return total;
}
for(int j=18;j>=0;j--)
{
if(f[x][j]!=f[y][j])
{
total=max(total,max(g[x][j],g[y][j]));
x=f[x][j];
y=f[y][j];
}
}
return total=max(max(g[x][0],g[y][0]),total);
}
int main()
{
scanf("%d%d",&n,&m);int q;scanf("%d",&q);
for(int i=1;i<=n;i++)
{
F[i]=i;
}
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].v);
}
sort(a+1,a+1+m,cmp);
num=0;
sum=0;
for(int j=1;j<=m;j++)
{
int fx=find(a[j].x);
int fy=find(a[j].y);
if(fx!=fy)
{
F[fx]=fy;
num++;
sum+=a[j].v;
add(a[j].x,a[j].y,a[j].v);
add(a[j].y,a[j].x,a[j].v);
}
if(num==n-1)
{
break;
}
}
g[1][0]=0;
dfs(1,1);
int A,B;
for(int i=1;i<=q;i++)
{
scanf("%d%d",&A,&B);
printf("%d\n",lca(A,B));
}
}

kruskal重构树

#include<map>
#include<set>
#include<queue>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
struct node
{
int x;
int y;
int z;
}a[30010];
int cnt;
int tot;
int A,B;
int n,m,k;
int d[30010];
int fa[15010];
int ga[30010];
int to[100010];
int head[100010];
int next[100010];
int f[30010][17];
int g[30010];
void add(int x,int y)
{
tot++;
next[tot]=head[x];
head[x]=tot;
to[tot]=y;
}
int get(int x)
{
if(ga[x]==x)
{
return x;
}
return ga[x]=get(ga[x]);
}
int find(int x)
{
if(fa[x]==x)
{
return x;
}
return fa[x]=find(fa[x]);
}
bool cmp(node a,node b)
{
return a.z<b.z;
}
void dfs(int x,int fa)
{
d[x]=d[fa]+1;
for(int i=1;i<=16;i++)
{
f[x][i]=f[f[x][i-1]][i-1];
}
for(int i=head[x];i;i=next[i])
{
if(to[i]!=fa)
{
dfs(to[i],x);
}
}
}
int lca(int x,int y)
{
if(d[x]<d[y])
{
swap(x,y);
}
int dep=d[x]-d[y];
for(int i=0;i<=16;i++)
{
if((dep&(1<<i))!=0)
{
x=f[x][i];
}
}
for(int i=16;i>=0;i--)
{
if(f[x][i]!=f[y][i])
{
x=f[x][i];
y=f[y][i];
}
}
x=f[x][0];
return g[x];
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++)
{
fa[i]=i;
}
for(int i=1;i<=2*n;i++)
{
ga[i]=i;
}
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
}
sort(a+1,a+1+m,cmp);
cnt=n;
for(int i=1;i<=m;i++)
{
int fx=find(a[i].x);
int fy=find(a[i].y);
if(fx!=fy)
{
fa[fx]=fy;
cnt++;
int gx=get(a[i].x);
int gy=get(a[i].y);
g[cnt]=a[i].z;
ga[gx]=cnt;
ga[gy]=cnt;
f[gx][0]=cnt;
f[gy][0]=cnt;
add(gx,cnt);
add(cnt,gx);
add(gy,cnt);
add(cnt,gy);
}
}
dfs(cnt,cnt);
for(int i=1;i<=k;i++)
{
scanf("%d%d",&A,&B);
printf("%d\n",lca(A,B));
}
}

BZOJ3732Network——kruskal重构树+倍增+LCA/最小生成树+倍增的更多相关文章

  1. 水壶-[Kruskal重构树] [解题报告]

    水壶 本来从不写针对某题的题解,但因为自己实在是太蠢了,这道题也神TM的恶心,于是就写篇博客纪念一下 H水壶 时间限制 : 50000 MS 空间限制 : 565536 KB 评测说明 : 2s,51 ...

  2. 【BZOJ 3732】 Network Kruskal重构树+倍增LCA

    Kruskal重构树裸题, Sunshine互测的A题就是Kruskal重构树,我通过互测了解到了这个神奇的东西... 理解起来应该没什么难度吧,但是我的Peaks连WA,,, 省选估计要滚粗了TwT ...

  3. 【BZOJ-3545&3551】Peaks&加强版 Kruskal重构树 + 主席树 + DFS序 + 倍增

    3545: [ONTAK2010]Peaks Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1202  Solved: 321[Submit][Sta ...

  4. BZOJ5415[Noi2018]归程——kruskal重构树+倍增+堆优化dijkstra

    题目描述 本题的故事发生在魔力之都,在这里我们将为你介绍一些必要的设定. 魔力之都可以抽象成一个 n 个节点.m 条边的无向连通图(节点的编号从 1 至 n).我们依次用 l,a 描述一条边的长度.海 ...

  5. BZOJ3545&3551[ONTAK2010]Peaks——kruskal重构树+主席树+dfs序+树上倍增

    题目描述 在Bytemountains有N座山峰,每座山峰有他的高度h_i.有些山峰之间有双向道路相连,共M条路径,每条路径有一个困难值,这个值越大表示越难走,现在有Q组询问,每组询问询问从点v开始只 ...

  6. LOJ #2718. 「NOI2018」归程(Dijkstra + Kruskal重构树 + 倍增)

    题意 给你一个无向图,其中每条边有两个值 \(l, a\) 代表一条边的长度和海拔. 其中有 \(q\) 次询问(强制在线),每次询问给你两个参数 \(v, p\) ,表示在 \(v\) 出发,能开车 ...

  7. Gym - 101173H Hangar Hurdles (kruskal重构树/最小生成树+LCA)

    题目大意:给出一个n*n的矩阵,有一些点是障碍,给出Q组询问,每组询问求两点间能通过的最大正方形宽度. 首先需要求出以每个点(i,j)为中心的最大正方形宽度mxl[i][j],可以用二维前缀和+二分或 ...

  8. UVA1265 Tour Belt Kruskal重构树、倍增、树上差分

    题目传送门 题意:定义$Tour \, Belt$为某张图上的一个满足以下条件的点集:①点集中至少有$2$个点②任意两点互相连通③图上两个端点都在这个点集中的边的权值的最小值严格大于图上只有一个端点在 ...

  9. [SCOI2013]摩托车交易 kruskal重构树(最大生成树) 倍增

    ---题面--- 题解: 这题想法简单,,,写起来真的是失智,找了几个小时的错误结果是inf没开到LL范围.... 首先我们需要找到任意两点之间能够携带黄金的上限值,因为是在经过的道路权值中取min, ...

随机推荐

  1. docker数据卷管理及网络基础配置

    数据卷 数据卷容器 数据卷迁移数据 端口映射 容器间通信 数据卷的管理 当需要查看容器内应用产生的数据或者把容器内数据备份及多个容器数据共享.有两种方式,数据卷以及数据卷容器. 数据卷 数据卷是一个可 ...

  2. JSON构造/解析(by C)---cJSON和json-c

    背景 JSON即JavaScript Object Notation,是一种轻量级的数据交换格式. JSON建构于两种结构: "名称/值"对的集合(A collection of ...

  3. CF258D Little Elephant and Broken Sorting/AGC030D Inversion Sum 期望、DP

    传送门--Codeforces 传送门--Atcoder 考虑逆序对的产生条件,是存在两个数\(i,j\)满足\(i < j,a_i > a_j\) 故设\(dp_{i,j}\)表示\(a ...

  4. 2018 Multi-University Training Contest 2 部分简单题解析

    Preface 多校第二场,依靠罚时优势打到了校内的Rank 2 暴力分块碾标算系列 T4 Game 题目大意:在一个数集\([1,n]\)中两个人轮流选择其中的一个数,并从数集中删去这个数所有约数. ...

  5. CF 888E Maximum Subsequence

    一道比较套路的题,看到数据范围就差不多有想法了吧. 题目大意:给一个数列和\(m\),在数列任选若干个数,使得他们的和对\(m\)取模后最大 取膜最大,好像不能DP/贪心/玄学乱搞啊.\(n\le35 ...

  6. 在python中使用正则表达式(一)

    在python中通过内置的re库来使用正则表达式,它提供了所有正则表达式的功能. 一.写在前面:关于转义的问题 正则表达式中用“\”表示转义,而python中也用“\”表示转义,当遇到特殊字符需要转义 ...

  7. [C#]SQLite执行效率优化结论

    一.如要使用SQLite,可以从Visual Studio中的“程序包管理器控制台”输入以下命令完成安装: PM> Install-Package System.Data.SQLite.Core ...

  8. Buy the Ticket HDU 1133

    传送门 [http://acm.hdu.edu.cn/showproblem.php?pid=1133] 题目描述和分析 代码 #include<iostream> #include< ...

  9. linux第三次读书笔记

    第七章:链接 一.编译器驱动程序 编译系统提供的调用预处理器.编译器.汇编器和链接器来构造目标文件的程序. 二.静态链接 三.目标文件 三种形式: 1.可重定位目标文件: 2.可执行目标文件: 3.共 ...

  10. jsp获取传过来的值

    request.setCharacterEncoding("utf-8"); String credit=request.getParameter("credit&quo ...