Luogu 2245 星际导航(最小生成树,最近公共祖先LCA,并查集)
Luogu 2245 星际导航(最小生成树,最近公共祖先LCA,并查集)
Description
sideman做好了回到Gliese 星球的硬件准备,但是sideman的导航系统还没有完全设计好。为了方便起见,我们可以认为宇宙是一张有N 个顶点和M 条边的带权无向图,顶点表示各个星系,两个星系之间有边就表示两个星系之间可以直航,而边权则是航行的危险程度。
sideman 现在想把危险程度降到最小,具体地来说,就是对于若干个询问(A, B),sideman 想知道从顶点A 航行到顶点B 所经过的最危险的边的危险程度值最小可能是多少。作为sideman 的同学,你们要帮助sideman 返回家园,兼享受安全美妙的宇宙航行。所以这个任务就交给你了。
对于40% 的数据,满足N≤1000,M≤3000,Q≤1000。
对于 80% 的数据,满足$$N≤104,M≤105,Q≤1000。$$
对于 100% 的数据,满足$$N≤105,M≤3×105,Q≤105,L≤109。$$数据不保证没有重边和自环。
Input
第一行包含两个正整数N 和M,表示点数和边数。
之后 M 行,每行三个整数A,B 和L,表示顶点A 和B 之间有一条边长为L 的边。顶点从1 开始标号。
下面一行包含一个正整数 Q,表示询问的数目。
之后 Q 行,每行两个整数A 和B,表示询问A 和B 之间最危险的边危险程度的可能最小值。
Output
对于每个询问, 在单独的一行内输出结果。如果两个顶点之间不可达, 输出impossible。
Sample Input
4 5
1 2 5
1 3 2
2 3 11
2 4 6
3 4 4
3
2 3
1 4
1 2
Sample Output
5
4
5
Http
Luogu:https://www.luogu.org/problem/show?pid=2245#sub
Source
最小生成树,最近公共祖先LCA,并查集
题目大意
在一张无向图上,求两点之间的一条路径使得路径上最大边权最小。
解决思路
笔者曾说过这种求最大值最小或最小值最大的问题多半是二分。
但这题不是的!
要解决这一题,首先要想明白的是为什么最后的解一定在该图的最小生成树上。(这个留给读者自己思考啦,虽然说确实看起来是的,但如何准确地证明呢?这是需要思考的)
然后我们就可以对要查询的两个点进行LCA啦。关于LCA的基本算法(倍增)请到我的这篇文章查看。
那么接下来的问题是,我们虽然求出了最近公共祖先,但我们并不知道这条路上的最大边权是多少啊?
这里我们引入一个新数组Path。Path[u][i]代表u到它的2^i祖先的路径上的最大边权。是不是觉得Path的定义与Parent有些相似呢?是的,这也是为了在倍增过程中方便地更新最后要求的值所定义的,并且它的求值与更新与Parent总是在一起的,过程也很类似,相信如果读者已经了解了LCA中Parent数组的求法,不难推导出Path的求法。
程序中要添加求Path数组的地方有两处
一是在dfs过程中,在得出Parent[v][0]的值得同时可以得出Path[v][0]=W[u][v](W[u][v]代表u-v这条边上的权值)
二是在for循环求Parent[i][j]=Parent[Parent[i][j-1]][j-1]时,同样可以递推出Path[i][j]=max(Path[i][j-1],Path[Parent[i][j-1]][j-1]),即i到2j祖先路径上的最大值等于i到2(j-1)上的最大值和i的2(j-1)祖先到i的2(j-1)祖先的2^(j-1)祖先的最大值这两者中的最大值。(有点绕,多读几遍就好了)
最后,在进行倍增上翻的过程中,每次更新a与b的值的同时,记录下最大的路径就可以了。
如果还有不理解,请结合下面的代码分析。
PS:话说这题是不是和货车运输进行了**交易
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
class Edge1
{
public:
int v,w;
};
class Edge2
{
public:
int u,v,w;
};
bool operator < (Edge2 a,Edge2 b)
{
return a.w<b.w;
}
const int maxN=100010;
const int maxM=300010;
const int inf=2147483647;
int n,m;
Edge2 E[maxM];
vector<Edge1> T[maxN];
//Union_Find_Set
int Mayuri[maxN];
//LCA
int Parent[maxN][25];
int Path[maxN][25];
int Depth[maxN];
bool vis[maxN];
int read();
void MST();
int Find(int u);
bool Union(int u,int v);
void LCA_init();
void dfs(int u);
int LCA(int a,int b);
int main()
{
n=read();m=read();
for (int i=1;i<=m;i++)
{
E[i].u=read();
E[i].v=read();
E[i].w=read();
}
MST();//求最小生成树
LCA_init();//LCA的各种信息初始化
int Q=read();
for (int i=1;i<=Q;i++)
{
int x=LCA(read(),read());
if (x==-1)
cout<<"impossible"<<endl;//注意无解的情况,即这两点不连通,可以用并查集判断
else
cout<<x<<endl;
}
return 0;
}
int read()//读入优化
{
int x=0;
int k=1;
char ch=getchar();
while (((ch<'0')||(ch>'9'))&&(ch!='-'))
ch=getchar();
if (ch=='-')
{
k=-1;
ch=getchar();
}
while ((ch>='0')&&(ch<='9'))
{
x=x*10+ch-48;
ch=getchar();
}
return x*k;
}
void MST()//求最小生成树,这里用克鲁斯卡尔算法
{
sort(&E[1],&E[m+1]);
for (int i=1;i<=n;i++)//并查集初始化
Mayuri[i]=i;
int cnt=0;
for (int i=1;i<=m;i++)
{
int u=E[i].u;
int v=E[i].v;
int w=E[i].w;
if (Union(u,v))
{
T[u].push_back((Edge1){v,w});
T[v].push_back((Edge1){u,w});
cnt++;
if (cnt==n-1)
break;
}
}
return;
}
int Find(int u)
{
if (Mayuri[u]!=u)
Mayuri[u]=Find(Mayuri[u]);
return Mayuri[u];
}
bool Union(int u,int v)
{
int fu=Find(u);
int fv=Find(v);
if (fu!=fv)
{
Mayuri[fu]=fv;
return 1;
}
return 0;
}
void LCA_init()
{
memset(Parent,0,sizeof(Parent));
memset(Path,0,sizeof(Path));
memset(Depth,0,sizeof(Depth));
memset(vis,0,sizeof(vis));
dfs(1);
for (int j=1;j<=20;j++)
for (int i=1;i<=n;i++)
{
Parent[i][j]=Parent[Parent[i][j-1]][j-1];
Path[i][j]=max(Path[i][j-1],Path[Parent[i][j-1]][j-1]);//同时求解Path
}
return;
}
void dfs(int u)
{
vis[u]=1;
for (int i=0;i<T[u].size();i++)
{
int v=T[u][i].v;
if (vis[v]==0)
{
Depth[v]=Depth[u]+1;
Parent[v][0]=u;
Path[v][0]=T[u][i].w;//记录Path的初值
dfs(v);
}
}
}
int LCA(int a,int b)
{
if (Find(a)!=Find(b))
{
return -1;
}
int max_path=0;
if (Depth[a]<Depth[b])
swap(a,b);
for (int i=20;i>=0;i--)
if ((Parent[a][i]!=0)&&(Depth[Parent[a][i]]>=Depth[b]))
{
max_path=max(max_path,Path[a][i]);//同时更新当前的最大边权
a=Parent[a][i];
}
if (a==b)
return max_path;
for (int i=20;i>=0;i--)
if ((Parent[a][i]!=0)&&(Parent[b][i]!=0)&&(Parent[a][i]!=Parent[b][i]))
{
max_path=max(max_path,Path[a][i]);//这里也是更新当前的最大边权
max_path=max(max_path,Path[b][i]);
a=Parent[a][i];
b=Parent[b][i];
}
max_path=max(max_path,Path[a][0]);//最后要注意再与Path[a][0]和Path[b][0]比较一下,因为在原来的LCA中,公共祖先是Parent[a][0]或Parent[b][0]
max_path=max(max_path,Path[b][0]);
return max_path;
}
Luogu 2245 星际导航(最小生成树,最近公共祖先LCA,并查集)的更多相关文章
- 学习笔记--最近公共祖先(LCA)的几种求法
前言: 给定一个有根树,若节点\(z\)是两节点\(x,y\)所有公共祖先深度最大的那一个,则称\(z\)是\(x,y\)的最近公共祖先(\(Least Common Ancestors\)),简称\ ...
- POJ 1470 Closest Common Ancestors(最近公共祖先 LCA)
POJ 1470 Closest Common Ancestors(最近公共祖先 LCA) Description Write a program that takes as input a root ...
- POJ 1330 Nearest Common Ancestors / UVALive 2525 Nearest Common Ancestors (最近公共祖先LCA)
POJ 1330 Nearest Common Ancestors / UVALive 2525 Nearest Common Ancestors (最近公共祖先LCA) Description A ...
- [模板] 最近公共祖先/lca
简介 最近公共祖先 \(lca(a,b)\) 指的是a到根的路径和b到n的路径的深度最大的公共点. 定理. 以 \(r\) 为根的树上的路径 \((a,b) = (r,a) + (r,b) - 2 * ...
- 最近公共祖先(LCA)模板
以下转自:https://www.cnblogs.com/JVxie/p/4854719.html 首先是最近公共祖先的概念(什么是最近公共祖先?): 在一棵没有环的树上,每个节点肯定有其父亲节点和祖 ...
- 【lhyaaa】最近公共祖先LCA——倍增!!!
高级的算法——倍增!!! 根据LCA的定义,我们可以知道假如有两个节点x和y,则LCA(x,y)是 x 到根的路 径与 y 到根的路径的交汇点,同时也是 x 和 y 之间所有路径中深度最小的节 点,所 ...
- 【CodeForces】827 D. Best Edge Weight 最小生成树+倍增LCA+并查集
[题目]D. Best Edge Weight [题意]给定n个点m条边的带边权无向连通图,对每条边求最大边权,满足其他边权不变的前提下图的任意最小生成树都经过它.n,m<=2*10^5,1&l ...
- jzoj4313 电话线铺设(最小生成树+最近公共祖先)
题面 \(solution:\) 这道题很奇妙,需要对kruskal重构树有足够的了解!我们先对王牌电缆实行kruskal重构树,然后我们再来枚举每一条李牌电缆,我们将某一条李牌电缆加进这棵树中必然构 ...
- 【原创】洛谷 LUOGU P3379 【模板】最近公共祖先(LCA) -> 倍增
P3379 [模板]最近公共祖先(LCA) 题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询 ...
随机推荐
- grok 匹配log4j
input { file { codec => multiline { pattern => "^\[2016" negate => true what => ...
- 【小练习03】CSS-表格(table)--天气预报
表格基础知识链接:http://blog.csdn.net/baidu_37107022/article/details/71713281 练习要求实现如下效果图: 代码演示 <!DOCTYPE ...
- 保存和恢复 Android Fragment 的状态
经过几年在 Android 应用开发中应用 Fragment 的努力之后,我必须要说尽管Fragment的概念非常优秀,但是它也同时带来了一堆问题.当我们处理实例的状态保存时就需要特别一件一件地修护好 ...
- canvas实现视频截图
截取视频当前播放画面,直接上源码. <body> <div class="container"> <video id="test" ...
- Vivado2015.4使用教程(一个完成工程的建立)
双击桌面的vivado图标,(可能有点慢) 弹出主菜单界面,点击create new project 这是介绍界面,next~ 添加好工程名,和工程位置,next~ 选择rtl Project,nex ...
- asp.net mvc中html helper的一大优势
刚上手这个框架,发现其中的html helper用起来很方便,让我们这些从web form 过渡来的coder有一种使用控件的快感,嘻嘻! 言归正传,我要说的是在使用它时,系统会自动执行表单的现场恢复 ...
- Linux下串口通信工具minicom的用法
一.查看串口设备 例如,将USB转串口线插入交换机Console口后,执行命令:$ll /dev/ttyUSB* 二.连接串口设备 $sudo minicom -D /dev/ttyUSB0 三.设置 ...
- flask 扩展之 -- flask-login
一. 使用 Werkzeug 实现密码散列. generate_password_hash(password, method=pbkdf2:sha1, salt_length=8) 将原始密码作为输入 ...
- SICP-1.6-高阶函数
高阶函数 将函数作为参数 例如 def sum_naturals(n): total, k = 0, 1 while k <= n: total, k = total + k, k + 1 re ...
- 就是要你懂Java中volatile关键字实现原理
原文地址http://www.cnblogs.com/xrq730/p/7048693.html,转载请注明出处,谢谢 前言 我们知道volatile关键字的作用是保证变量在多线程之间的可见性,它是j ...