带权LCA【洛谷P1967】

传送门: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;
}

猜你喜欢

转载自blog.csdn.net/KIKO_caoyue/article/details/84255512