题意:一棵树上有n个节点,给出m对节点,问他们到最近公共祖先的距离是多少
分析:关于LCA(最近公共祖先)问题,有许多种方案,比如说倍增、RMQ+时间戳、Tarjan、甚至是树链剖分等。其中Tarjan是一种离线算法,时间复杂度很喜人:
O
对于每一个点,我们保存它到根节点的距离
PS:由于是离线算法,那么我们需要一次将所有询问处理完,那么我们就用邻接表将询问保存;同样的,如果是强制在线问题(比如说在每次询问后会修改
LCA(x,y)
的边权),那么我们就需要用在线算法了!(首推倍增,详见博客未完成)
代码如下:
#include <bits/stdc++.h>
using namespace std;
struct node {
int to,c,net;
}edge[100010],q[150010];
int head[50010],qhead[50010];
int f[50010],c[50010],ans[75010],dis[50010];
bool vis[50010];
int n,m,tot1=0,tot2=0;
int read() {
int ans=0,flag=1;
char ch=getchar();
while((ch>'9' || ch<'0') && ch!='-') ch=getchar();
if(ch=='-') flag=-1,ch=getchar();
while(ch>='0' && ch<='9') ans=ans*10+ch-'0',ch=getchar();
return ans*flag;
}
void addedge1(int x,int y,int z) {
edge[++tot1].to=y;
edge[tot1].c=z;
edge[tot1].net=head[x];
head[x]=tot1;
return ;
}
void addedge2(int x,int y,int z) {
q[++tot2].to=y;
q[tot2].c=z;
q[tot2].net=qhead[x];
qhead[x]=tot2;
return ;
}
void input() { //建立邻接表
n=read();
for(int i=1;i<=n-1;i++) {
int a=read(),b=read(),c=read();
addedge1(a,b,c);
addedge1(b,a,c);
}
m=read();
for(int i=1;i<=m;i++) {
int a=read(),b=read();
addedge2(a,b,0);
addedge2(b,a,0);
}
return ;
}
int find(int x) { //和并查集的操作一致(顺便路径压缩)
if(x==f[x]) return x;
else return f[x]=find(f[x]);
}
void work(int fa,int son) { //这里是Tarjan部分
f[son]=son;
for(int i=head[son];i;i=edge[i].net) {
int to=edge[i].to;
if(to==fa) continue;
dis[to]=dis[son]+edge[i].c;
work(son,to);
f[to]=son;
}
vis[son]=true;
for(int i=qhead[son];i;i=q[i].net) {
int to=q[i].to;
if(vis[to]) {
ans[(i+1)/2]=dis[son]+dis[to]-2*dis[find(to)];
}
}
return ;
}
int main() {
input();
work(0,0);
for(int i=1;i<=m;i++) {
printf("%d\n",ans[i]);
}
return 0;
}
From:Chlience