题解 P2245 【星际导航】
kruskal 重构树,轻松最优解第一
这个东西主要来处理最小生成树的最大边权问题
当然也可以处理最大生成树的最小边权问题
其实这个重构树的核心思想跟
这张图是样例的最小生成树
而这张是重构树
我们看到右图里的重构树中多了一些方点
这些方点是怎么产生的呢
其实这些方点是原来最小生成树里的边
我们重构树的过程是这样的
-
将所有边按边权从小到大排序
-
每次最小的一条边,如果条边相连的两个点在同一个集合中,那么就跳过,否则就将这两个点的祖先都连到一个虚点上去,让这个虚点的点权等于这条边的边权
这样的话这课被重构的树就有一些奇妙的性质
-
原本最小生成树上的点在重构树里都是叶节点
-
从任何一个点往根上引一条路径,这条路径经过的点的点权单调不降(最大生成树单调不升)
-
任意两点之间路径的最大边权就是他们的LCA的点权
于是我们重构树之后找一下LCA就行了
这里用的是树剖,还是挺快的
代码
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define re register
#define maxn 200001
using namespace std;
struct node
{
int v,nxt,w;
}e[maxn<<2],a[maxn<<2];
int n,m,k,num,Q;
int fa[maxn],top[maxn],f[maxn],deep[maxn],head[maxn];
int sum[maxn],son[maxn],key[maxn];
inline int read()
{
char c=getchar();
int x=0;
while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9')
x=(x<<3)+(x<<1)+c-48,c=getchar();
return x;
}
inline int find(int x)
{
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}
inline int add_edge(int x,int y)
{
e[++num].v=y;
e[num].nxt=head[x];
head[x]=num;
}
void dfs1(int r)
{
sum[r]=1;
int maxx=-1;
for(re int i=head[r];i;i=e[i].nxt)
if(!deep[e[i].v])
{
deep[e[i].v]=deep[r]+1;
f[e[i].v]=r;
dfs1(e[i].v);
sum[r]+=sum[e[i].v];
if(sum[e[i].v]>maxx) maxx=sum[e[i].v],son[r]=e[i].v;
}
}
void dfs2(int r,int topf)
{
top[r]=topf;
if(!son[r]) return;
dfs2(son[r],topf);
for(re int i=head[r];i;i=e[i].nxt)
if(deep[e[i].v]>deep[r]&&son[r]!=e[i].v) dfs2(e[i].v,e[i].v);
}
inline int LCA(int x,int y)
{
while(top[x]!=top[y])
{
if(deep[top[x]]<deep[top[y]]) swap(x,y);
x=f[top[x]];
}
if(deep[x]<deep[y]) return x;
return y;
}
inline int cmp(node aa,node bb)
{
return aa.w<bb.w;
}
int main()
{
n=read();
m=read();
for(re int i=1;i<=m;i++)
{
a[i].v=read();
a[i].nxt=read();
a[i].w=read();
}
sort(a+1,a+m+1,cmp);
for(re int i=1;i<=(n<<1);i++) fa[i]=i;
k=n;
for(re int i=1;i<=m;i++)
{
int xx=find(a[i].v);
int yy=find(a[i].nxt);
if(xx==yy) continue;
fa[xx]=fa[yy]=++k;
add_edge(k,xx);
add_edge(xx,k);
add_edge(k,yy);
add_edge(yy,k);
key[k]=a[i].w;
}
for(re int i=k;i;i--)
if(!deep[i]) deep[i]=1,dfs1(i),dfs2(i,i);
Q=read();
int x,y;
while(Q--)
{
x=read();
y=read();
if(find(x)!=find(y)) puts("impossible");
else printf("%d",key[LCA(x,y)]),putchar(10);
}
return 0;
}