#include<bits/stdc++.h>
using namespace std;
const int maxn = 4e4+5;
struct Edge{
int v,next,val;
}edge[maxn*2]; //order 和 dep 需要开点的两倍 因为dfs序的时候记录两次
int fir[maxn],order[2*maxn],dep[2*maxn],head[maxn],dis[maxn],father[maxn][25];//fir表示节点第一次出现的dfs标号 order记录它的dfs序的标号包括了回溯时经过的
int cnt,tot,dp[2*maxn][50],log_2[2*maxn],depth[maxn];//真正的记录每个点的深度 与dep不同
void init(){//找lca方法 如果找a 和 b 点 先通过first数组得到两点第一次出现的dfs序号,然后在这个区间l r中找到depth[]数组记录的区间段中深度最小的点就是他们的父节点的深度
//然后根据depth[]数组这个时候的下标 再去order[]即dfs序遍历记录的数组中对应即可找到LCA
cnt = 0,tot=0;
memset(dp,0,sizeof(dp));
for(int i=0;i<maxn;i++){
fir[i],order[i]=0,dep[i]=0,head[i]=-1,dis[i]=0,log_2[i]=0;
}
}
void addedge(int from,int to,int val){
edge[cnt].v = to;
edge[cnt].next = head[from];
edge[cnt].val = val;
head[from] = cnt++;
}
void dfs(int x,int pre,int dep_,int val){
father[x][0]=pre;
dis[x] +=val;
order[++tot] = x;
dep[tot] = dep_;
depth[x] = depth[pre]+1;
fir[x] = tot;//oder记录第tot次的时候dfs经过的点 同时这次点的深度 fir记录该点第一次出现的tot序号
for(int i=head[x];i!=-1;i=edge[i].next){
int to = edge[i].v;
if(to==pre)continue;//又回到它的父節點了 所以跳過繼續
dfs(to,x,dep_+1,val+edge[i].val);
order[++tot] = x;
dep[tot] = dep_;
}
}
void ST(){
log_2[1]=0,dp[1][0]=1;
for(int i=2;i<=tot;i++){
dp[i][0] = i;//dp[i][j]记录RMQ的下标
log_2[i]=log_2[i-1];
if((1<<log_2[i]+1)==i)
++log_2[i];
}
for(int j=1;(1<<j)<=tot;j++){
for(int i=1;i+(1<<j)-1<=tot;i++){//区间是i,i+2^j-1 故长度是2^j
int mid = i + (1<<(j-1));
if(dep[dp[i][j-1]] < dep[dp[mid][j-1]]) dp[i][j] = dp[i][j-1];//选择深度小的
else dp[i][j] = dp[mid][j-1];
}
}
}
int RMQ(int x,int y){
int l = fir[x],r=fir[y];//第一次出现的时候dfs的下标
if(l>r)swap(l,r);
// while((1<<(k+1))<=r-l+1)k++;
int k = log_2[r-l+1];
// if(dep[dp[l][k]] < dep[dp[r-(1<<k)+1][k]])return dp[l][k];
// return dp[r-(1<<k)+1][k];
return dep[dp[l][k]] < dep[dp[r-(1<<k)+1][k]] ? dp[l][k]:dp[r-(1<<k)+1][k];
}
int find(int x, int k){
int now = x, tmp = 0;
while (k){
if (k & 1)
now = father[now][tmp];
k >>= 1;
tmp++;
}
return now;
} //从第x个点向上找k个类似快速幂
int main(){
int T;
scanf("%d",&T);
while(T--){
int n,m;
scanf("%d%d",&n,&m);
init();
for(int i=1;i<n;i++){
int x,y,val;
scanf("%d%d%d",&x,&y,&val);
addedge(x,y,val);
addedge(y,x,val);
}
int root = 1;
dfs(root,1,1,0);
for (int i = 1; i < 25; i++)
for (int j = 1; j <= n; j++)
father[j][i] = father[father[j][i - 1]][i - 1];
ST();
for(int i=1;i<=m;i++){
int x,y;
scanf("%d%d",&x,&y);
int ans = RMQ(x,y);//查询的点
int res = dis[x] + dis[y] - 2*dis[order[ans]];
// cout<<ans<<endl;
printf("%d %d\n",res,order[ans]);
}
}
}
/*
2
3 2
1 2 10
3 1 15
1 2
2 3
2 2
1 2 100
1 2
2 1
*/
LCA_ST算法
最新推荐文章于 2020-12-26 10:40:53 发布