1. 程式人生 > >帶權LCA【洛谷P1967】

帶權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;
}