题解
A - How far away ?
题目:有n个房子,两两之间有长度不一的路,求指定的a,b两个房子之间的距离(答案唯一)。
直接放上两种代码,里面都有解释。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
const int N=40000+20;
using namespace std;
struct node
{
int v,c;
}tmp;
vector<node>q[N];
vector<node>e[N];
int n,m,t;
int fa[N],sum[N],vis[N],ans[N];
//并查集初始化
void init()
{
memset(vis,0,sizeof(vis));
for(int i=1; i<=n; i++)
fa[i]=i;
}
//并查集查找
int find_(int x)
{
while(x!=fa[x])
x=fa[x];
return x;
}
int dfs(int u,int pre,int val)//初始:u->1,pre->-1,val->0
{
sum[u]=val;
for(int i=0; i<e[u].size(); i++)
{
int v=e[u][i].v;
int c=e[u][i].c;
if(v==pre)
continue;
dfs(v,u,val+c);
fa[v]=u;
}
for(int i=0; i<q[u].size(); i++)
{
int v=q[u][i].v;
if(vis[v])
{
int c=q[u][i].c;
int aim=find_(v);
ans[c]=sum[u]+sum[v]-2*sum[aim];//ans存的是计算出来的结果
}
}
vis[u]=1;
}
int main()
{
int u,v,c;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
init();
for(int i=1; i<n; i++)
{
scanf("%d%d%d",&u,&v,&c);
tmp.v=v,tmp.c=c;
e[u].push_back(tmp);//尾部加入一个数据
tmp.v=u;
e[v].push_back(tmp);
///至此,tem.v存的是u,e[u]->v和c,e[v]->u和c
}
//导入m次查询
for(int i=1; i<=m; i++)
{
scanf("%d%d",&u,&v);
tmp.v=v,tmp.c=i;
q[u].push_back(tmp);
tmp.v=u;
q[v].push_back(tmp);
///tem.v存的是u,q[u]->v和i,q[v]->u和i。此时,q所对应的c存的是ans数组需要的i
}
dfs(1,-1,0);
for(int i=1; i<=m; i++)//输出m次查询
printf("%d\n",ans[i]);
}
}
#include <cstdio>
#include <cstring>
using namespace std;
int const mx = 40010;
int n, m, cnt;
int x[mx], y[mx], z[mx];///x, y表示询问的起点和终点,z是x和y的LCA
int f[mx], dist[mx], pre[mx];///fa存祖先,dist存到根的距离,pre存父亲
bool vis[mx]; ///用来判断节点是否被访问过
struct Edge
{
int id, val; ///当前边序号,边权
int next; ///下一条
} e[2 * mx];
void AddEdge(int u, int v, int w) ///u->父节点 v->子节点 w->权值
{
e[cnt].id = u;
e[cnt].val = w;
e[cnt].next = pre[v];
pre[v] = cnt++;
e[cnt].id = v;
e[cnt].val = w;
e[cnt].next = pre[u];///pre数组存的就是父亲
pre[u] = cnt++;
}
int Find(int x)
{
return x == f[x] ? x : f[x] = Find(f[x]); ///当 x == f[x] 是正好就是两个数的共同父节点
}
void tarjan(int k) ///离线算法
{
vis[k] = true;
f[k] = k;
for(int i = 1; i <= m; i++)///如果当前节点是要访问的而且另一个节点已经被访问,就输出访问结果
{
if(x[i] == k && vis[y[i]])
z[i] = Find(y[i]);
if(y[i] == k && vis[x[i]])
z[i] = Find(x[i]);
}
for(int i = pre[k]; i != -1; i = e[i].next) ///往下遍历(跟左右)
{
if(!vis[e[i].id])
{
dist[e[i].id] = dist[k] + e[i].val;
tarjan(e[i].id);
f[e[i].id] = k;
}
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int u, v, w;
scanf("%d %d", &n, &m);
cnt = 0;
memset(pre, -1, sizeof(pre)); ///初始化
for(int i = 1; i < n; i++)
{
scanf("%d %d %d", &u, &v, &w);
AddEdge(u, v, w); ///把输入的边的关系还有距离增加上
}
for(int i = 1; i <= n; i++)
x[i] = y[i] = z[i] = 0; ///初始化起始点x[],终点y[],和x[i]和y[i]的LCA
for(int i = 1; i <= m; i++)
{
scanf("%d %d", &u, &v);
x[i] = u;
y[i] = v; ///记录起始点
}
memset(vis, false, sizeof(vis)); ///初始化所有的点都未访问
dist[1] = 0; ///根到根的距离是0
tarjan(1);
for(int i = 1; i <= m; i++)
printf("%d\n",dist[x[i]] + dist[y[i]] - 2 * dist[z[i]]); ///dist[]存的是节点到根的距离
}
return 0;
}