传送门:https://www.luogu.org/problemnew/show/P1967
带权LCA,模板题。
和fa[now][0]一样,w[now][0]表示父节点的权值。
更新w[now][i]的时候要从w[now][0]开始更新,因为上一次记录的是w[now][0],因为这个没写好错了很多次。
然后就是结点往上爬的时候更新w权值。
奥奥,为什么这个题跑一下LCA就可以了呢。
题目要求是找一条最小边的最大值,dijkstra+二分直接炸了,只有10分,跑了2W MS。
下面说说算法的正确性。
既然是最小边的最大值,那我们就直接按照边权排序构造一个最大生成树,这样显然可以保证最小值最大。
然后我们在最大生成树上跑LCA,因为在最大生成树上,任意两个点之间有且仅有一条路径,所以我们从起点和终点找LCA,然后更新一下两边最小的边权,就相当于从起点到终点的边权的最小值啦。
细节比较多,其实也没啥难度(装傻)
下面上代码:
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 1e5+7;
const int maxl = 21;
int n,m;
int depth[maxn],fa[maxn][maxl];
int w[maxn][maxl];
int lg[maxn];
int vis[maxn];
int p[maxn];
struct node
{
int from;
int to;
int cost;
}q[maxn];
struct edge
{
int to;
int cost;
};
vector<edge> G[maxn];
bool cmp(node a,node b)
{
return a.cost>b.cost;
}
void makeset()
{
for(int i=0;i<maxn;i++)
{
p[i] = i;
}
}
int find(int x)
{
if(x==p[x]) return x;
return p[x] = find(p[x]);
}
void unite(int x,int y)
{
x = find(x);
y = find(y);
if(x==y) return;
p[x] = y;
}
bool same(int x,int y)
{
return find(x)==find(y);
}
void kruskal()
{
makeset();
sort(q+1,q+1+m,cmp);
for(int i=1;i<=m;i++)
{
if(same(q[i].from,q[i].to)) continue;
unite(q[i].from,q[i].to);
G[q[i].from].push_back({q[i].to,q[i].cost});
G[q[i].to].push_back({q[i].from,q[i].cost});
}
}
void dfs(int now,int last)
{
vis[now] = 1;
depth[now] = depth[last]+1;
fa[now][0] = last;
for(int i=1;(1<<i)<=depth[now];i++)
{
w[now][i] = min(w[now][i-1],w[fa[now][i-1]][i-1]);
fa[now][i] = fa[fa[now][i-1]][i-1];
}
for(int i=0;i<G[now].size();i++)
{
if(G[now][i].to!=last)
{
w[G[now][i].to][0] = G[now][i].cost;
dfs(G[now][i].to,now);
}
}
}
int lca(int x,int y)
{
if(!same(x,y)) return -1;
if(depth[x]>depth[y]) swap(x,y);
int ans = INF;
while(depth[x]!=depth[y])
{
ans = min(ans,w[y][lg[depth[y]-depth[x]]-1]);
y = fa[y][lg[depth[y]-depth[x]]-1];
}
if(x==y) return ans;
for(int i=lg[depth[x]]-1;i>=0;i--)
{
if(fa[x][i]!=fa[y][i])
{
ans = min(ans,min(w[x][i],w[y][i]));
x = fa[x][i];
y = fa[y][i];
}
}
ans = min(ans,min(w[x][0],w[y][0]));
return ans;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
cin>>q[i].from>>q[i].to>>q[i].cost;
}
kruskal();
for(int i=1;i<=n;i++)
{
lg[i] = lg[i-1]+(1<<lg[i-1]==i);
}
for(int i=1;i<=n;i++)
{
if(!vis[i])
{
dfs(i,0);
}
}
int q;
cin>>q;
for(int i=0;i<q;i++)
{
int x,y;
cin>>x>>y;
printf("%d\n",lca(x,y));
}
return 0;
}