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;
}
发布了47 篇原创文章 · 获赞 4 · 访问量 1297

猜你喜欢

转载自blog.csdn.net/listenhhh/article/details/98883756