树上前缀和与差分(边权)

问题描述
有一棵n个点的树,每个点i有点树v[i],q个询问求点u到点v最简路径上所有点权之和
输入格式
第一行n,q表示有n个点,q个询问
第二行n个整数表示每个点的权
下面n-1每行三个整数x,y,z描述边的信息及边长
下面q行每行两个整数u,v表示询问的两个点路径长度
输出
q行,每行为u到v最简路径长度
样例

in
5 5
1 2 4
2 3 6
1 4 2
4 5 6
1 5
1 4
2 5
3 4
2 4
out
8
2
12
12
6

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
struct node{
	int to;
	int dis;
	int nxt;
}E[maxn<<1];
int h[maxn],dist[maxn],dep[maxn],f[maxn][25],lg[maxn],etot;
void addedge(int x,int y,int d)
{
	E[++etot].to=y;
	E[etot].dis=d;
	E[etot].nxt=h[x];
	h[x]=etot;
}
void dfs(int u,int fa)
{
	dep[u]=dep[fa]+1;
	f[u][0]=fa;
	for (int i=1;(1<<i)<=dep[u];i++)
	{
		f[u][i]=f[f[u][i-1]][i-1];
	}
	for (int i=h[u];i;i=E[i].nxt)
	{
		int v=E[i].to;
		if (v!=fa)
		{
			dist[v]+=dist[u]+E[i].dis;
			dfs(v,u);
		}
	}
}
int lca(int x,int y)
{
	if (dep[x]<dep[y]) swap(x,y);
	int d=dep[x]-dep[y];
	while(d)
	{
		int k=lg[d];
		x=f[x][k];
		d=d-(1<<k);
	}	
	if (x==y) return x;
	for (int i=lg[dep[x]];i>=0;i--)
	{
		if (f[x][i]!=f[y][i])
		{
			x=f[x][i];
			y=f[y][i];
		} 
	}
	return f[x][0];
} 
int main()
{
	int n,q;
	cin>>n>>q;
	for (int i=1;i<n;i++)
	{
		int x,y,d;
		cin>>x>>y>>d;
		addedge(x,y,d);
		addedge(y,x,d);
	}
	dfs(1,0);
	for (int i=1;i<=q;i++)
	{
		int x,y;
		cin>>x>>y;
		int l=lca(x,y);
		cout<<dist[x]+dist[y]-2*dist[l]<<endl;
	}
}

  

 

posted @ 2022-11-07 18:49  心悟&&星际  阅读(26)  评论(0编辑  收藏  举报