题目:http://acm.hdu.edu.cn/showproblem.php?pid=2586
题意:有一棵树,每次询问u,v的距离
思路:
- Tarjan算法(离线)
- ST算法(在线)
Tarjan算法代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N = 40005;
struct edge
{
int to,nxt,d;
edge(int t = 0,int n = 0,int d = 0):to(t),nxt(n),d(d){}
}E[N*2];
int n;
int head[N*2],vis[N],f[N],dis[N],ans[N],tot;
vector< pair<int,int> > q[N];
void init()
{
memset(head,-1,sizeof(head));
//memset(vis,0,sizeof(vis));
for(int i = 0;i < N;i++)
vis[i] = dis[i] = ans[i] = 0,f[i] = i;
tot = 0;
}
void add_edge(int s,int t,int d)
{
E[tot] = edge(t,head[s],d);
head[s] = tot++;
}
int getf(int u)
{
return u == f[u] ? u : getf(f[u]);
}
void merge(int u,int v)
{
int fu = getf(u);
int fv = getf(v);
if(fu != fv)
f[fv] = fu;
}
void tarjan(int u)
{
vis[u] = 1;
for(int i = head[u];~i;i = E[i].nxt)//访问所有子结点
{
int v = E[i].to,w = E[i].d;
if(!vis[v])
{
dis[v] = dis[u] + w;
tarjan(v);
merge(u,v);//合并v到u上
}
}
for(int i = 0;i < q[u].size();i++)//访问所有和u有关系的v
{
int v = q[u][i].first,w = q[u][i].second;
if(vis[v])
ans[w] = dis[u] + dis[v] - 2*dis[getf(v)];
}
}
int main()
{
int t,n,m;
scanf("%d",&t);
while(t--)
{
init();
scanf("%d%d",&n,&m);
int u,v,w;
for(int i = 1;i < n;i++)
{
scanf("%d%d%d",&u,&v,&w);
add_edge(u,v,w);
add_edge(v,u,w);
}
for(int i = 1;i <= m;i++)
{
scanf("%d%d",&u,&v);
q[u].push_back({v,i});
q[v].push_back({u,i});
}
tarjan(1);
for(int i = 1;i <= m;i++)
printf("%d\n",ans[i]);
}
return 0;
}
ST算法代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N = 40005;
struct edge
{
int to,nxt,d;
edge(int t = 0,int n = 0,int d = 0):to(t),nxt(n),d(d){}
}E[N*2];
int n;
int head[N*2],tot;
int cnt,vis[N],f[N*2],rk[N*2],pos[N],dis[N],dp[N*2][35];
//f存储节点编号,rk存储节点深度,pos记录结点第一次出现的位置
void init()
{
memset(head,-1,sizeof(head));
//memset(vis,0,sizeof(vis));
for(int i = 0;i < N;i++)
vis[i] = dis[i] = 0;
tot = cnt = 0;
}
void add_edge(int s,int t,int d)
{
E[tot] = edge(t,head[s],d);
head[s] = tot++;
}
void dfs(int u,int depth)
{
vis[u] = 1;
f[++cnt] = u;
pos[u] = cnt;
rk[cnt] = depth;
for(int i = head[u];~i;i = E[i].nxt)//访问所有子结点
{
int v = E[i].to,w = E[i].d;
if(!vis[v])
{
dis[v] = dis[u] + w;
dfs(v,depth+1);
f[++cnt] = u;
rk[cnt] = depth;
}
}
}
void RMQ(int n)
{
for(int i = 1;i <= n;i++)
dp[i][0] = i;
for(int j = 1;(1<<j) <= n;j++)
{
for(int i = 1;i+(1<<j)-1 <= n;i++)
{
int a = dp[i][j-1],b = dp[i + (1<<j-1)][j-1];
dp[i][j] = rk[a] < rk[b] ? a : b;
}
}
}
int query(int l,int r)
{
int k = (int)(log(r - l + 1.0) / log(2.0));
int a = dp[l][k],b = dp[r-(1<<k)+1][k];
return rk[a] < rk[b] ? a : b;
}
int LCA(int u,int v)
{
int x = pos[u],y = pos[v];
if(x > y) swap(x,y);
int t = query(x,y);//找到深度最小的结点编号
return f[t];
}
int main()
{
int t,n,m;
scanf("%d",&t);
while(t--)
{
init();
scanf("%d%d",&n,&m);
int u,v,w;
for(int i = 1;i < n;i++)
{
scanf("%d%d%d",&u,&v,&w);
add_edge(u,v,w);
add_edge(v,u,w);
}
dfs(1,1);
RMQ(cnt);//cnt条边
while(m--)
{
scanf("%d%d",&u,&v);
int lca = LCA(u,v);
printf("%d\n",dis[u] + dis[v] - 2*dis[lca]);
}
}
return 0;
}