avatar
fireworks99
keep hungry keep foolish

HDU 2586 How far away(LCA)

Description

n个点,n - 1条边连起来(构成了一棵树),无更新操作,查询任意两点间距离

Analyze

https://blog.csdn.net/nameofcsdn/article/details/52230548

Code(RMQ~ST离线做法)

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 41000;

int n, m;
int tot;//遍历数
int dis[N];//距离根节点1的距离


struct node
{
    int v, w, pre;
} a[N * 2];
int head[N * 2], cnt = 1;

void add_edge(int u, int v, int w)
{
    a[cnt].v = v;
    a[cnt].w = w;
    a[cnt].pre = head[u];
    head[u] = cnt++;
}


bool vis[N];
int first[N];//该点首次出现所对应的的遍历数tot
int point[N * 4];//遍历数tot所对应的那个点的标号
int depth[N * 4];//遍历数tot所对应的那个点的深度
void DFS(int u, int dep)
{
    vis[u] = 1;
    point[++tot] = u, first[u] = tot, depth[tot] = dep;
    for(int i = head[u]; ~i; i = a[i].pre)
    {
        int v = a[i].v, w = a[i].w;
        if(!vis[v])
        {
            dis[v] = dis[u] + w;
            DFS(v, dep + 1);
            //退回到这一点也是一次访问
            point[++tot] = u, depth[tot] = dep;
        }
    }
}


int sma[N][20];//存的是某个区间里最小的遍历数tot
void ST()
{
    for(int i = 1; i <= n; ++i)
        sma[i][0] = depth[i];
    for(int j = 1; (1 << j) <= n; ++j)
        for(int i = 1; i + (1 << j) - 1 <= n; ++i)
            sma[i][j] = min(sma[i][j - 1], sma[i + (1 << (j - 1))][j - 1]);
}


int RMQ(int l, int r)
{
    int k = 0;
    while( (1 << (k + 1)) <= r - l + 1 )
        ++k;
    return min(sma[l][k], sma[r - (1 << k) + 1][k]);
}


int LCA(int u, int v)
{
    int x = first[u];
    int y = first[v];
    if(x > y)
        swap(x, y);
    int res = RMQ(x, y);
    return point[res];
}


int main()
{
    int _;
    scanf("%d", &_);
    while(_--)
    {
        cnt = 1;
        scanf("%d%d", &n, &m);
        int u, v, w;
        memset(head, -1, sizeof(head));
        for(int i = 0; i < n - 1; ++i)
        {
            scanf("%d%d%d", &u, &v, &w);
            add_edge(u, v, w);
            add_edge(v, u, w);
        }
        tot = 0, dis[1] = 0;
        memset(vis, 0, sizeof(vis));
        DFS(1, 1);
        ST();
        while(m--)
        {
            int u, v;
            scanf("%d%d", &u, &v);
            int lca = LCA(u, v);
            cout << dis[u] + dis[v] - 2 * dis[lca] << '\n';
        }
    }
    return 0;
}

Trajan+并查集(在线做法)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 41000;
const int M = 810;

int n, m;
bool vis[N];
int ance[N];//准lca
int pre[N], dis[N];


struct edge
{
    int v, w, pre;
} a[N * 2];
int head[N * 2], cnt;

void add_adge(int u, int v, int w)
{
    a[cnt].v = v;
    a[cnt].w = w;
    a[cnt].pre = head[u];
    head[u] = cnt++;
}


struct query
{
    int u, v, lca, pre;
} q[N];
int qhead[N * 2], qcnt;

void add_query(int u, int v)
{
    q[qcnt].u = u;
    q[qcnt].v = v;
    q[qcnt].lca = -1;
    q[qcnt].pre = qhead[u];
    qhead[u] = qcnt++;
}


int found(int x)
{
    if(x != pre[x])
        pre[x] = found(pre[x]);
    return pre[x];
}

void unite(int a, int b)
{
    int x = found(a);
    int y = found(b);
    if(x != y)
        pre[y] = x;
}


void Tarjan(int u)
{
    vis[u] = 1;
    ance[u] = pre[u] = u;
    for(int i = head[u]; ~i; i = a[i].pre)
    {
        int v = a[i].v;
        int w = a[i].w;
        if(!vis[v])
        {
            dis[v] = dis[u] + w;
            Tarjan(v);
            unite(u, v);//Tarjan完了再unite,保证了LCA是准确的
        }
    }
    for(int i = qhead[u]; ~i; i = q[i].pre)
    {
        int v = q[i].v;
        if(vis[v])//v是已经遍历过的非叶子节点
            q[i].lca = q[i ^ 1].lca = ance[found(v)];
    }
}

void init()
{
    cnt = qcnt = 0;//从零开始,小偶大奇,异或1
    memset(vis, 0, sizeof(vis));
    memset(dis, 0, sizeof(dis));
    memset(head, -1, sizeof(head));
    memset(qhead, -1, sizeof(qhead));
}

int main()
{
    int _;
    scanf("%d", &_);
    while(_--)
    {
        init();
        scanf("%d%d", &n, &m);
        int u, v, w;
        for(int i = 0; i < n - 1; ++i)
        {
            scanf("%d%d%d", &u, &v, &w);
            add_adge(u, v, w);
            add_adge(v, u, w);
        }
        for(int i = 0; i < m; ++i)
        {
            scanf("%d%d", &u, &v);
            add_query(u, v);
            add_query(v, u);
        }
        dis[1] = 0;
        Tarjan(1);
        for(int i = 0; i < m; ++i)//0 1/2 3/4 5/...取前者
        {
            int u = q[i * 2].u;
            int v = q[i * 2].v;
            int lca = q[i * 2].lca;
            cout << dis[u] + dis[v] - 2 * dis[lca] << '\n';
        }
    }
    return 0;
}

在建树的时候就把LCA求出来

Site by Baole Zhao | Powered by Hexo | theme PreciousJoy