題解
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;
}