题目描述

给你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. 【Codeforces 204E】Little Elephant and Strings

    Codeforces 204 E 题意:给\(n\)个串,求对于每一个串在至少\(k\)个串中出现的它的子串\(S_{l..r}\)有多少个. 思路:后缀自动机上\(dp\)... 我们首先构造出这\ ...

  2. Android学习之基础知识三(Android日志工具Log的使用)

    Android中的日志工具Log(android.util.Log): 1.打印日志的方法(按级别从低到高排序): Log.v():级别verbose,用于打印最为烦琐,意义最小的日志 Log.d() ...

  3. PHP实现栈数据结构

    利用php面向对象思想,栈的属性有top.最大存储数.和存储容器(这里利用了php数组). 代码如下:实现了入栈.出栈.遍历栈的几个方法: <?php class Stack{ const MA ...

  4. git log 的常用选项

  5. EF 利用PagedList进行分页并结合查询 方法2

    微软提供了PagedList分页,相信大家在网上也能搜索一大堆关于pagedList用法的博客,论坛.但是,在使用的过程中一不小心,就会掉入pagedList某种常规用法的陷阱. 我所说的某种常规用法 ...

  6. NOI Day1线上同步赛梦游记

    Preface 第一次体验NOI,虽然不是正式选手,但是打打同步赛还是挺涨姿势的,也算是体验了一把. Day1很爆炸,一方面是NOI题目的难度高于自身的水平,另一方面也出现了比较大的失误,T1一个数组 ...

  7. pandas:字段值插入数据表第一行的解决办法

    1. 问题描述 在对课程表进行数据抽取时,由于课表结构的原因,需要在原始表字段名作为第一行数据,并对原始字段名进行替换. 原始数据如下所示: 2. 解决办法 经思考,此问题可抽象为:在不影响原始数据的 ...

  8. (8)学习笔记 ) ASP.NET CORE微服务 Micro-Service ---- Ocelot网关(Api GateWay)

    说到现在现有微服务的几点不足: 1) 对于在微服务体系中.和 Consul 通讯的微服务来讲,使用服务名即可访问.但是对于手 机.web 端等外部访问者仍然需要和 N 多服务器交互,需要记忆他们的服务 ...

  9. Echo团队便利记事本项目终审报告

    一.团队成员简介 http://www.cnblogs.com/echo-buaa/p/3991968.html 二.团队项目的目标,预期的典型用户,预期的功能描述,预期的用户数量在哪里? 项目的目标 ...

  10. 个人阅读作业Week5

    一.总结体会 团队项目已经进行了很多周,我们团队从刚开始的基础薄弱到现在的大家都可以运用Android来编写程序,共同完成一个app的开发使用. 刚开始做团队项目之时,我们团队就开了一个会,确定了以后 ...