点的距离 tarjanLCA模板题 P5020
Description
给定一个有n个结点的树,Q个询问,每次询问点x与点y之间的最短距离。
Input
第一行一个n,接下来n-1行,每行两个整数x,y,表示x,y之间有一条边。然后是Q,接下来Q行每行两个数x,y 表示询问x到y的距离
Output
输出Q行,每行针对每个询问的结果
Hint
n,Q <=10^5
Solution
太坑了,我觉得我跟tarjan有仇。。。。他的算法我从来都没有独立搞懂过啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊。。。。。。我太弱了。。。
嗯首先就是求最近公共祖先LCA的话tarjan离线算法就是把DFS跟并查集结合起来了(可惜我并查集全忘了。。。)然后大概思路就是把当前DFS到的点的父亲结点全部更新成DFS的u这个点,然后如果这个时候已经找到了需要询问的LCA就直接更新这个询问的LCA的值,然后如果没有的话就要把FA的值变成还要往上的父亲结点,然后继续去tarjan,最后就能得到所有需要询问的LCA,只不过的话必须要知道所有需要询问的两个点,所以tarjan是离线算法。
注意事项:
1.n个结点的树有n-1条边所以输入边的时候循环结束n不取等。。。
2.这道题要求的是两个结点之间的距离,所以求距离就是两个结点的depppp的和-LCA的depppp值的二倍。
3.无向图用静态链表存的话他的序号是1 3 5 7…所以最后那个循环是for(int i=1;i<=qqq*2;i+=2)。。。
4.例行,我是傻逼。。。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define maxn 200005
using namespace std;
struct Edge{
int u;
int v;
int next;
}edge[maxn],q[maxn];
int first[maxn],last[maxn],qf[maxn],ql[maxn],FA[maxn],lcaaaa[maxn],depppp[maxn];
int node1,node2,n,qqq,x,y;
bool Vis_edge[maxn];
void addedge(int u,int v){
edge[++node1]=(Edge){u,v,0};
if(first[u]==0)first[u]=node1;
else edge[last[u]].next=node1;
last[u]=node1;
}
void addquery(int u,int v){
q[++node2]=(Edge){u,v,0};
if(qf[u]==0)qf[u]=node2;
else q[ql[u]].next=node2;
ql[u]=node2;
}
int find_Faaaaa(int s){
if(FA[s]!=s){
FA[s]=find_Faaaaa(FA[s]);
}
return FA[s];
}
void tarjannnnn(int x,int faa){
depppp[x]=depppp[faa]+1;
FA[x]=x;
Vis_edge[x]=true;
for(int i=first[x];i;i=edge[i].next){
int v=edge[i].v;
if(!Vis_edge[v]){
tarjannnnn(v,x);
FA[v]=x;
}
}
for(int j=qf[x];j;j=q[j].next){
int v=q[j].v;
if(Vis_edge[v]){
lcaaaa[j]=find_Faaaaa(v);
}
if(j%2){
lcaaaa[j+1]=lcaaaa[j];
}
else{
lcaaaa[j-1]=lcaaaa[j];
}
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<n;i++){
scanf("%d%d",&x,&y);
addedge(x,y);
addedge(y,x);
}
scanf("%d",&qqq);
for(int i=1;i<=qqq;i++){
scanf("%d%d",&x,&y);
addquery(x,y);
addquery(y,x);
}
tarjannnnn(1,0);
for(int i=1;i<=qqq*2;i+=2){
printf("%d\n",depppp[q[i].u]+depppp[q[i].v]-2*depppp[lcaaaa[i]]);
}
return 0;
}