#1067 : 最近公共祖先·二

一道LCA的模板题。

代码:

#include <cstdio>
#include <algorithm>
#include<map>
#include<string.h>
#include<iostream>
#define MAXN 1000010
#define MAXM 1000010
using namespace std;
map<string,int>MAP;
map<int,string>MAP2;
struct Edge
{
    int from, to, next;
};
Edge edge[MAXM];
int head[MAXN], edgenum;
int vs[MAXN<<1];//第i次DFS访问节点的编号
int depth[MAXN<<1];//第i次DFS访问节点的深度
int id[MAXN];//id[i] 记录在vs数组里面 i节点第一次出现的下标
int dfs_clock;//时间戳
int N, M, Q;//点数 边数 查询数
int dp[MAXN<<1][20];//dp[i][j]存储depth数组  以下标i开始的,长度为2^j的区间里 最小值所对应的下标
void init()
{
    edgenum = 0;
    memset(head, -1, sizeof(head));
}
void addEdge(int u, int v)
{
    Edge E = {u, v, head[u]};
    edge[edgenum] = E;
    head[u] = edgenum++;
}
void getMap()
{
    int cnt=0;
    int a, b;
    string h1,h2;
    while(M--)
    {
        cin>>h1>>h2;
        if(!MAP[h1])
        {
            MAP[h1]=++cnt;
            MAP2[cnt]=h1;
        }
        if(!MAP[h2])
        {
            MAP[h2]=++cnt;
            MAP2[cnt]=h2;
        }
        addEdge(MAP[h1], MAP[h2]), addEdge(MAP[h2], MAP[h1]);
    }
}
void DFS(int u, int fa, int d)//当前遍历点以及它的父节点  遍历点深度
{
    id[u] = dfs_clock;
    vs[dfs_clock] = u;
    depth[dfs_clock++] = d;
    for(int i = head[u]; i != -1; i = edge[i].next)
    {
        int v = edge[i].to;
        if(v == fa) continue;
        DFS(v, u, d+1);
        vs[dfs_clock] = u;//类似 回溯
        depth[dfs_clock++] = d;
    }
}
void find_depth()
{
    dfs_clock = 1;
    memset(vs, 0, sizeof(vs));
    memset(id, 0, sizeof(id));
    memset(depth, 0, sizeof(depth));
    DFS(1, -1, 0);//遍历
}
void RMQ_init(int NN)//预处理 区间最小值
{
    for(int i = 1; i <= NN; i++)
        dp[i][0] = i;
    for(int j = 1; (1<<j) <= NN; j++)
    {
        for(int i = 1; i + (1<<j) - 1 <= NN; i++)
        {
            int a = dp[i][j-1];
            int b = dp[i + (1<<(j-1))][j-1];
            if(depth[a] <= depth[b])
                dp[i][j] = a;
            else
                dp[i][j] = b;
        }
    }
}
int query(int L, int R)
{
    //查询L <= i <= R 里面使得depth[i]最小的值 返回对应下标
    int k = 0;
    while((1<<(k+1)) <= R-L+1) k++;
    int a = dp[L][k];
    int b = dp[R - (1<<k) + 1][k];
    if(depth[a] <= depth[b])
        return a;
    else
        return b;
}
int LCA(int u, int v)
{
    int x = id[u];//比较大小 小的当作左区间 大的当作右区间
    int y = id[v];
    if(x > y)
        return vs[query(y, x)];
    else
        return vs[query(x, y)];
}
void solve()
{
    string a, b;
    while(Q--)
    {
        cin>>a>>b;
        int p=LCA(MAP[a], MAP[b]);
        cout<<MAP2[p]<<endl;
    }
}
int main()
{
    scanf("%d",&M);
        init();
        getMap();
        find_depth();//DFS遍历整个树 求出所需要的信息
        RMQ_init(dfs_clock - 1);
    scanf("%d",&Q);
        solve();
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值