POJ 1986 Distance Queries

/*
由于输入图是一个树,所以可以任意指定一个节点为root结点,然后用LCATarjan算法离线计算任意被查询两个点的LCA,则:
dist(i, j) = dist(root, i) + dist(root, j) - 2 * dist(root, lca(i, j))
寻找LCA的Tarjan算法,LCATarjan算法是一个离线算法,即所有查找请求是一次性同时处理的,而不是输入
一个处理一个.Tarjan算法主要用到了并查集和DFS,利用并查集来在DFS往上返回的时候改变结点自身的LCA.
LCATarjan的主要框架是:
lcaTarjan(int curnodeid, int curdist) //curdist表示root到curnodeid的距离
{
    dist(root, curnodeid) = curdist; 
    lca(curnodeid) = curnodeid; //第一次访问到某个节点就将其lca置为自身

    for(all request related to curnodeid) //遍历所有与curnodeid有关的查询请求
    {
        int toid = get opposite point; //得到查询的另一个端点
        if(request has not been processed && toid has been visited)
        {
            int lca = find(toid); //利用并查集寻找toid与curnodid当前lca;
            dist(curnodeid, toid) = dist(root, curnodid) + dist(root, toid) - 2 * dist(root, lca(curnodeid, roid));
        }
    }
    for(all curnodid's sons sid)
    {
        if(!v[sid]) lcaTarjan(sid, curdist + edgeweight(curnodid, sid));
        lca(sid) = curnodid; //dfs返回时置sid的lca为父亲curnodid;
    }
}
  */


#include <iostream>
#include <vector>
#define MAX_N 40005
#define MAX_Q 10005
using namespace std;

struct edge
{
    int to, dist;
    edge *nexte;
    edge()
    {
        nexte = NULL;
    }
};

struct querry
{
    int from, to, dist;
    bool v;
}querrys[MAX_Q + 1];

struct node
{
    int sid, dist;
    bool v;
    edge *nexte;
    vector<querry*> q;
}nodes[MAX_N + 1];

int fn, rn, qn;

void insertEdge(int from, int to, int dist)
{
    edge *newedge = new edge();
    newedge->to = to; newedge->dist = dist;
    if(!nodes[from].nexte) nodes[from].nexte = newedge;
    else{
        edge *curedge = nodes[from].nexte;
        while(curedge->nexte)
            curedge = curedge->nexte;
        curedge->nexte = newedge;
    }
}

int find(int nid)
{
    if(nid == nodes[nid].sid) return nid;
    else return nodes[nid].sid = find(nodes[nid].sid);
}

void lcaTarjan(int curid, int curdist)
{
    nodes[curid].dist = curdist;
    nodes[curid].sid = curid;

    vector<querry*>::iterator iter = nodes[curid].q.begin();
    for(; iter != nodes[curid].q.end(); iter++)
    {
        int toid = (*iter)->from + (*iter)->to - curid;
        if((*iter)->v || !nodes[toid].v) continue;
        int lca = find(toid);
        (*iter)->dist = nodes[curid].dist + nodes[toid].dist - 2 * nodes[lca].dist;
        (*iter)->v = true;
    }

    edge *curedge = nodes[curid].nexte;
    while(curedge)
    {
        if(!nodes[curedge->to].v)
        {
            nodes[curedge->to].v = true;
            lcaTarjan(curedge->to, curdist + curedge->dist);
            nodes[curedge->to].sid = curid;
        }
        curedge = curedge->nexte;
    }
}
int main()
{
    int i, from, to, dist;
    char dir;
    scanf("%d%d", &fn, &rn);
    for(i = 0; i < rn; i++)
    {
        scanf("%d%d%d", &from, &to, &dist);
        getchar();
        scanf("%c", &dir);
        insertEdge(from, to, dist);
        insertEdge(to, from, dist);
    }
    scanf("%d", &qn);
    for(i = 0; i < qn; i++)
    {
        scanf("%d%d", &from, &to);
        querrys[i].from = from; querrys[i].to = to;
        nodes[from].q.push_back(&querrys[i]);
        nodes[to].q.push_back(&querrys[i]);
    }
    nodes[1].v = true;
    lcaTarjan(1, 0);
    for(i = 0; i < qn; i++)
        printf("%d/n", querrys[i].dist);
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值