题目连接:戳我
题目大意,给定一个树,树上的边有权值。然后下面一个m,m次询问,每次询问两个点,然后对于每个询问输出他们的距离。
一直没找到好的LCA裸题,别人推荐给我一个,于是乎做了一下,处理father数组的同时,记录一个S数组,保存从i点网上跳2的j次方的路径上的权值和,查询的时候再求一下ans就可以了。如果看不懂的话,在代码里面尽量会说明的…………
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int size = 100010;
int fat[size][32];
int s[size][32];
int deep[size];
int eto[size],edist[size],head[size],next[size],tot;
int n;
void build(int f,int t,int d) //建树函数,同建图一样建树。
{
eto[++tot] = t;
edist[tot] = d;
next[tot] = head[f];
head[f] = tot;
}
void dfs(int u,int d)
{
/*
dfs,第一个参数表示当前节点,第二个参数表示深度
*/
int tmp = deep[u] = d; //深度数组,tmp可以去掉,是我调试的时候用的
for(int i = head[u];i;i = next[i])
{
int v = eto[i];
if(!deep[v]) //如果v没有被访问过
{
fat[v][0] = u;
s[v][0] = edist[i];
dfs(v,d+1);
/*
v向上跳2^0次方,也就是他的父亲,就是u,因为v是u的儿子,所以u是v的父亲
从v向上跳1的距离就是这条边的权值
然后递归处理他的儿子
*/
}
}
}
inline void init()
{
int k = log2(n);
for(int j = 1;j <= k;j ++)
{
for(int i = 1;i <= n;i ++)
{
fat[i][j] = fat[fat[i][j-1]][j-1];
s[i][j] = s[fat[i][j-1]][j-1]+s[i][j-1];
/*
利用倍增的思想,第一句话的意思就是,i向上跳2^j = i向上跳2^(j-1)再跳2^(j-1)次方
第二句话的意思就是,i向上跳2^j的距离 = (i向上跳2^(j-1))(fat[i][j-1]的含义)向上跳(j-1)次方的距离+i向上跳2^(j-1)的距离
*/
}
}
}
int find(int a,int b)
{
if(deep[a] > deep[b]) swap(a,b); //初始保证左边小,为了下面准备
int k = log2(n);
int ans = 0;
for(int i = k;i >= 0;i --)
{
if(deep[fat[b][i]] < deep[a]) continue;
//上面意思是,如果跳后,深度小于a了,说明跳多了,我们的目的是让他们深度相等,所以不管他
ans += s[b][i];
b = fat[b][i];
//如果满足条件,ans加上跳的这段距离,更新b节点
}
//此时他们深度相同
for(int i = k;i >= 0;i --)
{
if(fat[a][i] == fat[b][i]) continue;
//如果他们跳后父亲相同了,这时候可能跳过了,我们也不管他
ans += s[a][i];
ans += s[b][i];
a = fat[a][i];
b = fat[b][i];
//如果满足条件,ans同时加上两边跳的距离,更新两个节点
}
//这时候他们可能同一个点也可能不同点
if(a != b)
{
//如果不同点,那他们的父亲就是LCA,再跳一步即可,ans加上两边的权值,结束
ans += s[a][0];
ans += s[b][0];
}
//返回答案
return ans;
}
int main()
{
scanf("%d",&n);
for(int i = 1;i < n;i ++)
{
int u,v,c;
scanf("%d%d%d",&u,&v,&c);
u ++;
v ++;
build(v,u,c);
build(u,v,c);
}
dfs(1,1);
init();
// for(int i = 1;i <= n;i ++) printf("deep[%d] : %d\n",i,deep[i]);
int m;
scanf("%d",&m);
for(int i = 1;i <= m;i ++)
{
int l,r;
scanf("%d%d",&l,&r);
l ++;
r ++;
int sum = find(l,r);
printf("%d\n",sum);
}
return 0;
}
QAQ