一道LCA的练手题。
题目大意,在一个树中给出每两点之间的距离,然后让你求任意两点间的距离。
很明显这可以用树剖(但是我不会),但是因为这里没有涉及到修改,我们可以巧妙的利用LCA来解决这个问题
记一个点x到树根的距离为dis[x],另一个店y到树根的距离为dis[y],运用一下简单的容斥原理就可以知道x,y之间的最短路为:dis[x]+dis[y]-2*dis[LCA(x,y)]
所以我们用Tarjan(倍增,DFS序+RMQ也行)预处理出所有的dis值以及每一对x,y的LCA,在询问的时候O(1)查询即可。
预处理的复杂度为O(n α(n)),后面那个是并查集的复杂度
CODE
#include<cstdio> #include<cstring> using namespace std; const int N=40005; struct edge { int to,next,v; }e[N]; struct ques { int to,next,num; }q[N]; struct data { int x,y,fa; }a[N]; int t,n,m,i,k,qk,x,y,z,root,father[N],dis[N],head[N],qhead[N],ru[N]; bool vis[N]; inline void read(int &x) { x=0; char ch=getchar(); while (ch<'0'||ch>'9') ch=getchar(); while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); } inline void write(int x) { if (x/10) write(x/10); putchar(x%10+'0'); } inline void add(int x,int y,int z) { e[++k].to=y; e[k].v=z; e[k].next=head[x]; head[x]=k; } inline void qadd(int x,int y,int z) { q[++qk].to=y; q[qk].num=z; q[qk].next=qhead[x]; qhead[x]=qk; } inline int getfather(int k) { return father[k]==k?k:father[k]=getfather(father[k]); } inline void DFS(int now) { for (int i=head[now];i!=-1;i=e[i].next) dis[e[i].to]=dis[now]+e[i].v,DFS(e[i].to); } inline void LCA(int now) { vis[now]=1; for (int i=qhead[now];i!=-1;i=q[i].next) if (vis[q[i].to]) a[q[i].num].fa=getfather(q[i].to); for (int i=head[now];i!=-1;i=e[i].next) LCA(e[i].to),father[e[i].to]=now; } int main() { read(t); while (t--) { memset(head,-1,sizeof(head)); memset(e,-1,sizeof(head)); memset(qhead,-1,sizeof(qhead)); memset(q,-1,sizeof(q)); memset(ru,0,sizeof(ru)); memset(vis,0,sizeof(vis)); read(n); read(m); for (i=1;i<n;++i) { read(x); read(y); read(z); add(x,y,z); ru[y]++; } for (i=1;i<=n;++i) if (!ru[i]) { root=i; break; } dis[root]=0; DFS(root); for (i=1;i<=n;++i) father[i]=i; for (i=1;i<=m;++i) { read(a[i].x); read(a[i].y); qadd(a[i].x,a[i].y,i), qadd(a[i].y,a[i].x,i); } LCA(root); for (i=1;i<=m;++i) write(dis[a[i].x]+dis[a[i].y]-2*dis[a[i].fa]),putchar(' '); } return 0; }