/*少说话,多做事*/ #include <cstdio> #include <cstring> #include <string> #include <cmath> #include <iostream> #include <algorithm> #include <vector> #include <stack> #include <sstream> #include <map> #include <set> #include <queue> #include <stdlib.h> //#include<bits/stdc++.h> typedef long long ll; using namespace std; /* n (房子的数量)m(询问的数量) n-1行 i j k: 表示房子i与j之间的距离是k m行(m次询问)i j : 表示i与j之间的距离是多少 解题关键: ans=dis[u]+dis[v]−2∗dis[lca] 1. 任选一个点为根节点,从根节点开始 2. 遍历该点u所有子节点v,并标记这些子节点v已被访问过 3. 若v还有子节点,返回2,否则进行下一步 4. 合并v到u上 5. 寻找与当前点u有询问关系的点v 6. 若是v已经被访问过了,则可以确认u和v的最近公共祖先为v被合并到的父亲节点a */ double eps=1e-10; const ll N=1e5+10; //边的数量(双向边,记住结构体要*2) struct sss { int to,next,w; //分别代表的是终点、next、权值 }edge[N*2]; int ans[250]; //询问次数不多于200 int val[N]; //用于记录点到根节点的距离 int vis[N]; //用于标记 2:这个点已经遍历过并且回溯过了,1:这个点遍历过但是还没有回溯 ,0:表示这个点还没有遍历 int fa[N]; int n,m; vector<int>q[N],id[N]; //vector -> q记录相关联的点,id记录询问的序号(因为所有的询问都是一起输入的,所以用id记录询问的序号,便于最后的输出) int find(int x) { if(fa[x]==x) return x; else return fa[x]=find(fa[x]); } int cnt=1; int head[N]; //head数组初始化-1 void add(int u,int v,int w) //链式向前星 { edge[cnt].to=v; edge[cnt].next=head[u]; edge[cnt].w=w; head[u]=cnt++; } void dfs(int x) //从根节点(x)出发开始遍历 { vis[x]=1; //标记x已经被遍历过了 for(int i=head[x]; i>=0 ; i=edge[i].next) //遍历与x节点连通的点 { int y=edge[i].to; // y是这条边的终点 if(vis[y]!=0) continue; //如果y已经被标记过了,那么continue; val[y]=val[x]+edge[i].w; //y到根节点的距离=x到根节点的距离+两者之间边的权值 dfs(y); //继续对y节点进行子节点的遍历 fa[y]=x; //如果遍历多次过后,y节点没有子节点了,那么将y与x节点连接起来(x节点变为y的父亲节点) } /* 如果没有找到与x点的子节点,就是上一个for循环直接不进行,那么就找跟x有关联的点,如果跟y有关联的点vis=0,continue, vis=1,找到两者的公共祖先是 跟y有关联的点的祖先(切记,一定是更新完fa数组之后才进行关联点的寻找) */ for(int i=0; i<q[x].size(); i++) //找跟x相关联的点(如果没有的话,这一步直接就没有了) { int idd=id[x][i]; int z=q[x][i]; //z是与x相关联的点 if(vis[z]==2) //如果z被遍历过且回溯过,那么就可以找x和z的公共祖先 { int w=find(z); //w为两者的公共祖先(并查集) ans[idd]=min(ans[idd],val[x]+val[z]-2*val[w]); //两个询问节点到根节点x的距离-2*最近公共祖先w到根节点x的距离 } } vis[x]=2; //将x记录为即遍历又回溯过 } void init() { cnt=1; memset(head,-1,sizeof(head)); //head数组初始化为-1 memset(vis ,0,sizeof(vis)); for(int i=0;i<=n;i++) { fa[i]=i; q[i].clear(); id[i].clear(); } } int main() { int t; cin >> t; while(t--) { cin >> n >> m; //点的个数、询问的个数 init(); int x,y,z; for(int i=1;i<n;i++) //n-1次加边 { cin >> x >> y >> z; add(x,y,z); //添加双向边 add(y,x,z); } for(int i=1;i<=m;i++) //一共m次询问,询问x,y的最近公共祖先 { cin >> x >> y; //x与y有关系 /* 与x相关联的点是y,与y相关联的点是x */ q[x].push_back(y); //在vector->q中, 将y加入x ,将x加入y (将x放入y中,将y放入x中)(q[x]是从0开始计数的,所以找的时候从[0~q[x].size) q[y].push_back(x); /* x,y的询问编号都是i */ id[x].push_back(i); //在vector->id中,将i加入x ,将i加入y (将第i条边的序号i放入x,y中) id[y].push_back(i); ans[i]=0x3f3f3f3f; //每个询问最后的结果都是最大值 } dfs(1); for(int i=1;i<=m;i++) //一共m次询问,每个询问输出一个两个点到最近公共祖先的最短距离 { cout << ans[i] << endl; } } return 0; }