输入描述 Input Description
第一行一个n,接下来n-1行每一行有三个整数u,v, c 。表示节点 u 爬到节点 v 需要花费 c 的精力。
第n+1行有一个整数m表示有m次询问。接下来m行每一行有两个整数 u ,v 表示两只虫子所在的节点
输出描述 Output Description
一共有m行,每一行一个整数,表示对于该次询问所得出的最短距离。
算法:倍增LCA。
大致思路:建边时双向建,值得注意的是根结点要自己选择,这里选择的是0。用grand【i】【j】表示i结点2^j处的祖先,dis【i】【j】表示它与祖先的距离。然后跳时注意dis的改变方式。
几点提醒:LCA一定要记住
1.双向边,开结构体时maxx要左推一位。
2.20到0是i--,不是++。
3.grand数组,如果要play20,数组至少第二维开21,最好到22。
4.一定一定要先dfs root结点。
↑都是语言没学好的锅 = =
代码:
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxx = 50000 + 100;
int head[maxx],depth[maxx];
int grand[maxx][20+2],dis[maxx][20+2];
bool done[maxx];
int n,m,x,y,z,num,root;
struct Edge{
int next;
int to;
int value;
}Edges[maxx<<1];
inline int Read(){
int x=0,f=1;char c=getchar();
while(c>'9'||c<'0') {if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9') {x=x*10+c-'0'; c=getchar();}
return x*f;
}
void Add(int x,int y,int z){
Edges[++num].to=y;
Edges[num].next=head[x];
Edges[num].value=z;
head[x]=num;
}
void Dfs(int x){
done[x]=true;
for(int i=1;i<=20;i++){
if((1<<i) > depth[x]) break;
grand[x][i]=grand[grand[x][i-1]][i-1];
dis[x][i]=dis[x][i-1]+dis[grand[x][i-1]][i-1];
}
for(int i=head[x];i;i=Edges[i].next){
int now=Edges[i].to;
if(done[now]) continue;
depth[now]=depth[x]+1;
grand[now][0]=x;
dis[now][0]=Edges[i].value;
Dfs(now);
}
}
inline int Lca(int x,int y){
int Ans=0;
if(depth[x]>depth[y]) swap(x,y);
int d=depth[y]-depth[x];
for(int i=0;i<=20;i++){
if((1<<i) & d) {
Ans += dis[y][i];
y=grand[y][i];
}
}
for(int i=20;i>=0;i--){
if(grand[x][i]!=grand[y][i]){
Ans += dis[x][i];
Ans += dis[y][i];
x=grand[x][i];
y=grand[y][i];
}
}
if(x==y) return Ans;
else{
Ans+=dis[x][0]+dis[y][0];
return Ans;
}
}
int main(){
n=Read();
for(int i=1;i<n;i++){
x=Read();y=Read();z=Read();
Add(x,y,z);Add(y,x,z);
}
Dfs(0);
m=Read();
while( m-- ){
x=Read();y=Read();
printf("%d\n",Lca(x,y));
}
return 0;
}