ACM新手DAY 23 dfs序和LCA

題解

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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章