题意:
给出一个带权无向图,询问两个点之间得距离。
分析:
求出DFS求出每个节点到根节点的距离,然后计算出最近公共祖先设为X,那么可以知道答案为 dis[u]+dis[v]-2*dis[X]
直接套用LCA就可求出解。
Tarjan
/*
* LCA离线算法,Tarjan
* 复杂度O(n+Q);
*/
#include<bits/stdc++.h>
using namespace std;
const int maxn = 4e4+10;
const int maxq = 210;//查询数的最大值
//并查集部分
int fa[maxn];//需要初始化
int find(int x){
return fa[x]==x?x:fa[x]=find(fa[x]);
}
void bing(int u,int v){
int t1 = find(u);
int t2 = find(v);
if(t1 != t2)
fa[t2] = t1;
}
//************************
bool vis[maxn];//访问标记
struct Edge{
int to,next,w;
}edge[maxn*2];
int head[maxn],tot;
void addedge(int u,int v,int w){
edge[tot].to = v;
edge[tot].w = w;
edge[tot].next = head[u];
head[u] = tot++;
}
struct Query{
int u,q,next;
int index;//查询编号
}query[maxq*2];
int answer[maxq];//存储最后的查询结果,下标0~Q-1
int h[maxn];
int tt;
int Q;
void add_query(int u,int v,int index){
query[tt].u=u;
query[tt].q = v;
query[tt].next = h[u];
query[tt].index = index;
h[u] = tt++;
query[tt].u=v;
query[tt].q = u;
query[tt].next = h[v];
query[tt].index = index;
h[v] = tt++;
}
void init(){
tot = 0;
memset(head,-1,sizeof(head));
tt = 0;
memset(h,-1,sizeof(h));
memset(vis,false,sizeof(vis));
for(int i=0;i<maxn;i++)fa[i]=i;
}
int dis[maxn];
void LCA(int u){
vis[u] = true;
for(int i = head[u];i != -1;i = edge[i].next){
int v = edge[i].to,w=edge[i].w;
if(vis[v])continue;
dis[v]=dis[u]+w;
LCA(v);
bing(u,v);
fa[v]=u;
}
for(int i = h[u];i != -1;i = query[i].next){
int v = query[i].q;
if(vis[v]){
answer[query[i].index] = find(v);
}
}
}
int main(){
int n,T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&Q);
init();
int u,v,w;
for(int i=1;i<n;i++){
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
addedge(v,u,w);
}
for(int i=0;i<Q;i++){
scanf("%d%d",&u,&v);
add_query(u,v,i);
}
dis[1]=0;
LCA(1);
for(int i=0;i<tt;i+=2){
printf("%d\n",dis[query[i].u]+dis[query[i].q]-2*dis[answer[query[i].index]]);
}
}
return 0;
}
倍增
/*
*
* LCA 在线算法
*/
#include<bits/stdc++.h>
using namespace std;
const int maxn = 4e4+10;
const int DEG = 20;
struct Edge{
int to,next,w;
}edge[maxn*2];
int head[maxn],tot;
void addedge(int u,int v,int w){
edge[tot].to = v;
edge[tot].w = w;
edge[tot].next = head[u];
head[u] = tot++;
}
void init(){
tot = 0;
memset(head,-1,sizeof(head));
}
int fa[maxn][DEG];//fa[i][j]表示结点i的第2^j个祖先
int deg[maxn];//深度数组
int dis[maxn];
void bfs(int root){
queue<int>que;
deg[root] = 0;
fa[root][0] = root;
que.push(root);
while(!que.empty()){
int tmp = que.front();
que.pop();
for(int i = 1;i < DEG;i++)
fa[tmp][i] = fa[fa[tmp][i-1]][i-1];
for(int i = head[tmp]; i != -1;i = edge[i].next){
int v = edge[i].to,w=edge[i].w;
if(v == fa[tmp][0])continue;
dis[v]=dis[tmp]+w;
deg[v] = deg[tmp] + 1;
fa[v][0] = tmp;
que.push(v);
}
}
}
int LCA(int u,int v){
if(deg[u] > deg[v])swap(u,v);
int hu = deg[u], hv = deg[v];
int tu = u, tv = v;
for(int det = hv-hu, i = 0; det ;det>>=1, i++)
if(det&1)
tv = fa[tv][i];
if(tu == tv)return tu;
for(int i = DEG-1; i >= 0; i--){
if(fa[tu][i] == fa[tv][i])
continue;
tu = fa[tu][i];
tv = fa[tv][i];
}
return fa[tu][0];
}
int main(){
int T;
scanf("%d",&T);
while(T--){
init();
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
addedge(v,u,w);
}
dis[1]=0;
bfs(1);
while(m--){
int u,v;
scanf("%d%d",&u,&v);
printf("%d\n",dis[u]+dis[v]-2*dis[LCA(u,v)]);
}
}
return 0;
}