题目描述
sideman 做好了回到Gliese 星球的硬件准备,但是 sideman 的导航系统还没有完全设计好。为了方便起见,我们可以认为宇宙是一张有 N 个顶点和 M 条边的带权无向图,顶点表示各个星系,两个星系之间有边就表示两个星系之间可以直航,而边权则是航行的危险程度。
sideman 现在想把危险程度降到最小,具体地来说,就是对于若干个询问 (A, B)sideman 想知道从顶点 A 航行到顶点 B 所经过的最危险的边的危险程度值最小可能是多少。作为 sideman 的同学,你们要帮助sideman 返回家园,兼享受安全美妙的宇宙航行。所以这个任务就交给你了。
输入格式
第一行包含两个正整数 N 和 M,表示点数和边数。
之后 M 行,每行三个整数 A,B 和 L,表示顶点 A 和 B 之间有一条边长为 L 的边。顶点从 1 开始标号。
下面一行包含一个正整数 Q,表示询问的数目。
之后 Q 行,每行两个整数 A 和 B,表示询问 A 和 B 之间最危险的边危险程度的可能最小值。
输出格式
对于每个询问, 在单独的一行内输出结果。如果两个顶点之间不可达, 输出 impossible。
输入输出样例
输入 #1
4 5
1 2 5
1 3 2
2 3 11
2 4 6
3 4 4
3
2 3
1 4
1 2
输出 #1
5
4
5
说明/提示
对于 40% 的数据,满足N≤1000,M≤3000,Q≤1000。
对于 80% 的数据,满足 N≤10000,M≤10^5,Q≤1000。
对于 100% 的数据,满足N≤10 ^ 5,M≤3×10 ^ 5 ,Q≤10 ^ 5 ,L≤10 ^ 9 。数据不保证没有重边和自环。
解法:kruskal重构树+树剖求LCA
前言:第一次听说这东西,感觉很神奇,其实也不算很难,下面引用这个大佬在洛谷上关于kruskal重构树的解释
重构树的过程
-
将所有边按边权从小到大排序
-
每次最小的一条边,如果条边相连的两个点在同一个集合中,那么就跳过,否则就将这两个点的祖先都连到一个虚点上去,让这个虚点的点权等于这条边的边权
重构树的性质
-
原本最小生成树上的点在重构树里都是叶节点
-
从任何一个点往根上引一条路径,这条路径经过的点的点权单调不降(最大生成树单调不升)
-
任意两点之间路径的最大边权就是他们的LCA的点权
可以解决的问题
- 最小生成树上的最大边权
- 最大生成树上的最小边权
然后就没什么了,直接放代码了
AC代码
#include<cstdio>
#include<algorithm>
#define si 300005
#define re register int
using namespace std;
struct edge {
int nex,to,w;
}e[si<<1],a[si<<1];
int n,m,k,num,q,top[si],head[si],key[si];
int f[si],d[si],fa[si],son[si],sum[si];
inline int read() {
int x=0,cf=1;
char ch=getchar();
while(ch<'0'||ch>'9') {
if(ch=='-') cf=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') {
x=(x<<3)+(x<<1)+(ch^48);
ch=getchar();
}
return x*cf;
}
inline bool cmp(edge A,edge B) { return A.w<B.w; }
inline void dfs1(int x) {
sum[x]=1; int maxx=-1;
for(re i=head[x];i;i=e[i].nex) {
int y=e[i].to;
if(d[y]) continue;
d[y]=d[x]+1,f[y]=x; dfs1(y);
sum[x]+=sum[y];
if(sum[y]>maxx) maxx=sum[y],son[x]=y;
}
}
inline void dfs2(int x,int topf) {
top[x]=topf;
if(!son[x]) return; dfs2(son[x],topf);
for(re i=head[x];i;i=e[i].nex) {
int y=e[i].to;
if(y==f[x]||y==son[x]) continue;
dfs2(y,y);
}
}
inline int lca(int x,int y) {
while(top[x]!=top[y]) {
if(d[top[x]]<d[top[y]]) swap(x,y);
x=f[top[x]];
}
if(d[x]<d[y]) return x; return y;
}
inline int find(int x) {
return fa[x]==x?x:fa[x]=find(fa[x]);
}
inline void add(int x,int y) {
e[++num].to=y,e[num].nex=head[x],head[x]=num;
}
int main() {
n=read(),m=read();
for(re i=1;i<=(n<<1);i++) fa[i]=i;
for(re i=1;i<=m;i++) {
a[i].nex=read();
a[i].to=read();
a[i].w=read();
}
sort(a+1,a+m+1,cmp); k=n;
for(re i=1;i<=m;i++) {
int xx=find(a[i].nex),yy=find(a[i].to);
if(xx==yy) continue;
fa[xx]=fa[yy]=++k;
add(k,xx),add(xx,k);
add(k,yy),add(yy,k);
key[k]=a[i].w;
}
for(re i=k;i;i--) {
if(!d[i]) {
d[i]=1,dfs1(i),dfs2(i,i);
}
}
q=read(); while(q--) {
int x=read(),y=read();
if(find(x)!=find(y)) printf("impossible\n");
else printf("%d\n",key[lca(x,y)]);
}
return 0;
}