daimayuan路径最小值

95 阅读1分钟

给一棵n个点的树,每条边上有边权。

给q个询问u,v,每次询问路径u,v上的最小值。

输入格式

第一行两个整数n,q(1≤n,q≤2×105)。

接下来n−1行,每行两个整数u,v,w(1≤w≤109)表示一条边的两个端点和边权。

接下来q行,每行两个整数u,v(u≠v,1≤u,v≤n)表示询问的两个端点。

输出格式

对于每组询问,输出一个数表示答案。

样例输入

5 5
1 2 3
2 3 4
2 4 5
3 5 2
4 5
1 3
2 4
1 5
1 4

样例输出

2
3
5
2
3

思路:用倍增的思想,从2的0次方,2的一次方,2的2次方......依次记录下来在进行比较

代码

#include <bits/stdc++.h>

using namespace std;

#define int long long

const int N = 2e5 + 10,LOGN = 30;
vector<pair<int,int>>e[N];
int n,q;
int dep[N],p[N][LOGN+1],val[N][LOGN + 1];
void dfs(int u,int f)
{
	dep[u] = dep[f] + 1;
	//cout << u << ' ' << f << '\n';
	for(auto [x,y]:e[u])
	{
		if(x == f)
		continue;
		p[x][0] = u;
		val[x][0] = y;
		dfs(x,u);
	}
}
int query(int u,int v)
{
	int ans = 1<<30;
	if(dep[u] > dep[v])
	swap(u,v);
	int d = dep[v] - dep[u];
	for(int j = LOGN;j >= 0;j --)
	if(d&(1<<j))
	{
		ans = min(ans,val[v][j]);
		v = p[v][j];
	}
	if(u == v)
	return ans;
	for(int j = LOGN;j >= 0;j --)
	if(p[u][j]!=p[v][j])
	{
		ans = min(ans,min(val[u][j],val[v][j]));
		u = p[u][j];
		v = p[v][j];
	}
	ans = min(ans,min(val[u][0],val[v][0]));
	return ans;
}
void solve()
{
	cin >> n >> q;
	for(int i = 1;i < n;i ++)
	{
		int u,v,w;
		cin >> u >> v >> w;
		e[u].push_back({v,w});
		e[v].push_back({u,w});
	}
	// for(int i = 1;i <= n;i ++)
	// {
		// for(int j = 0;j < e[i].size();j ++)
		// cout << e[i][j].first <<' ';
		// cout << '\n';
	// }
	dfs(1,0);
	for(int j = 1;j <= LOGN; j ++)
	{
		for(int u = 1;u <= n;u ++)
		{
			p[u][j] = p[p[u][j-1]][j-1];
			//cout << p[u][j] << ' ' << u << ' ' << j << ' ' <<p[u][j-1]<<'\n'; 
			val[u][j] = min(val[u][j-1],val[p[u][j-1]][j-1]);
		}
	}
	
	// for(int i = 1;i <= n;i ++)
	// {
		// for(int j = 1;j <= 18;j ++)
		// cout << p[i][j] << ' ' << i << ' ' << j << '\n';
	// }
	for(int i = 1;i <= q;i ++)
	{
		int u,v;
		cin >> u >> v;
		cout << query(u,v) << '\n';
	}
}
signed main()
{
	solve();
}