题目传送门 | |
---|---|
用并查集优化向上标记法
dfs过程中,建立一个用1,2染色的v数组
回溯的点标记为2,访问完毕未回溯标记为1,
正在访问的点,它到根节点的路径为1,若y是回溯过的点,则lca(x,y)就是y向上走到根第一个遇到1的点
不断缩小ans
#include<iostream>
#include<cmath>
#include<string>
#include<vector>
using namespace std;
const int N=1e6;
int h[N],e[N],w[N],ne[N],idx;
int n,m,T;
int f[N],v[N],d[N];//染色
//离线算法, 把输入存进去;
vector<int> query[N],query_id[N];
int ans[N];
void add(int a,int b,int c)
{
e[idx]=b;
w[idx]=c;
ne[idx]=h[a];
h[a]=idx++;
}
int get(int x)
{
return x==f[x]?x:f[x]=get(f[x]);
}
void tarjan(int x)
{
v[x]=1;
for(int i=h[x];i!=-1;i=ne[i])
{
int y=e[i];
if(v[y]) continue;
d[y]=d[x]+w[i];//搜到深处 记录d数组;
tarjan(y);
f[y]=x;
// 建立好1 2染色·的树;
}
for(int i=0;i<query[x].size();i++)
{
int y=query[x][i];
int idx=query_id[x][i];
if(v[y]==2)
{
int lca=get(y);
ans[idx]=min(ans[idx],d[x]+d[y]-2*d[lca]);
}
}
v[x]=2; //回溯的点标记为2;
}
int main()
{
cin>>T;
while(T--)
{
cin>>n>>m;
for(int i=1;i<=n;i++) f[i]=i,v[i]=0,h[i]=-1;
for(int i=1;i<=n;i++)
{
query[i].clear();
query_id[i].clear();
}
idx=0;
for(int i=1;i<n;i++)
{
int x,y,z;
cin>>x>>y>>z;
add(x,y,z);
add(y,x,z);
}
for(int i=1;i<=m;i++)
{
int x,y;
cin>>x>>y;
if(x==y) ans[i]=0;
else{
ans[i]=1<<30;
query[x].push_back(y);
query[y].push_back(x);
query_id[x].push_back(i);
query_id[y].push_back(i);
}
}
tarjan(1);
for(int i=1;i<=m;i++) cout<<ans[i]<<endl;
}
return 0;
}