【LCA求最短距离】poj 1986 Distance Queries

6 篇文章 0 订阅
Distance Queries
Time Limit: 2000MS
Memory Limit: 30000K
Total Submissions: 14738
Accepted: 5181
Case Time Limit: 1000MS

Description

Farmer John's cows refused to run in his marathon since he chose a path much too long for their leisurely lifestyle. He therefore wants to find a path of a more reasonable length. The input to this problem consists of the same input as in "Navigation Nightmare",followed by a line containing a single integer K, followed by K "distance queries". Each distance query is a line of input containing two integers, giving the numbers of two farms between which FJ is interested in computing distance (measured in the length of the roads along the path between the two farms). Please answer FJ's distance queries as quickly as possible!

Input

* Lines 1..1+M: Same format as "Navigation Nightmare"

* Line 2+M: A single integer, K. 1 <= K <= 10,000

* Lines 3+M..2+M+K: Each line corresponds to a distance query and contains the indices of two farms.

Output

* Lines 1..K: For each distance query, output on a single line an integer giving the appropriate distance.

Sample Input

7 6
1 6 13 E
6 3 9 E
3 5 7 S
4 1 3 N
2 4 20 W
4 7 2 S
3
1 6
1 4
2 6

Sample Output

13
3
36

Hint

Farms 2 and 6 are 20+3+13=36 apart.


LCA求最短路,模板题

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std;
const int maxn = 40005; //顶点数
const int maxq = 10010; //最多查询次数,根据题目而定,本题中其实每组数据只有一个查询.

//并查集
int f[maxn];//根节点
int find(int x)
{
    if (f[x] == -1)
    {
        return x;
    }
    return f[x] = find(f[x]);
}
void unite(int u, int v)
{
    int x = find(u);
    int y = find(v);
    if (x != y)
    {
        f[x] = y;
    }
}
//并查集结束

bool vis[maxn];//节点是否访问
int ancestor[maxn];//节点i的祖先
struct Edge
{
    int to, next, len;
} edge[maxn * 2];
int head[maxn], tot;
void addedge(int u, int v, int length) //邻接表头插法加边
{
    edge[tot].to = v;
    edge[tot].len = length;
    //  printf("%d  ",length);
    edge[tot].next = head[u];
    head[u] = tot++;
}

struct Query
{
    int q, next;
    int index;//查询编号,也就是输入的顺序
} query[maxq * 2];
int ans[maxn * 2]; //存储每次查询的结果,下表0~Q-1,其实应该开maxq大小的。
int h[maxn], tt;
int Q;//题目中需要查询的次数
int dis[maxn];

void addquery(int u, int v, int index) //邻接表头插法加询问
{
    query[tt].q = v;
    query[tt].next = h[u];
    query[tt].index = index;
    h[u] = tt++;
    query[tt].q = u; //相当于两次查询,比如查询  3,5 和5,3结果是一样的,以3为头节点的邻接表中有5,以5为头节点的邻接表中有3
    query[tt].next = h[v];
    query[tt].index = index;
    h[v] = tt++;
}
void init()
{
    tot = 0;
    memset(head, -1, sizeof(head));
    tt = 0;
    memset(h, -1, sizeof(h));
    memset(vis, 0, sizeof(vis));
    memset(f, -1, sizeof(f));
    memset(ancestor, 0, sizeof(ancestor));
    memset(dis, 0, sizeof(dis));
}
void LCA(int u)
{
    ancestor[u] = u;
    vis[u] = true;


    for (int i = head[u]; i != -1; i = edge[i].next) //和顶点u相关的顶点
    {
        int v = edge[i].to;

        // printf("%d    ",edge[i].len);
        if (vis[v])
        {
            continue;
        }
        //dis[v]+=edge[i].len;
        dis[v] = dis[u] + edge[i].len;
        LCA(v);

        unite(u, v);
        ancestor[find(u)] = u; //将u的左右孩子的祖先设为u
        //dis[find(u)]+=dis[u];

    }
    for (int i = h[u]; i != -1; i = query[i].next) //看输入的查询里面有没有和u节点相关的
    {
        int v = query[i].q;
        if (vis[v])
        {
            ans[query[i].index] = ancestor[find(v)];
        }
    }
}
bool flag[maxn];//用来确定根节点的

int t;
int n, m, u, v, len;
char cc;
int main()
{
    scanf("%d%d", &n, &m);
    init();
    memset(flag, 0, sizeof(flag));
    for (int i = 1; i <= m; i++)
    {
        scanf("%d%d%d %c", &u, &v, &len, &cc);
        flag[v] = true; //有入度
        addedge(u, v, len);
        addedge(v, u, len);
    }
    scanf("%d", &Q);
    for (int i = 0; i < Q; i++)
    {
        scanf("%d%d", &u, &v);
        addquery(u, v, i);
    }
    int root;
    for (int i = 1; i <= n; i++)
    {
        if (!flag[i])
        {
            root = i;
            //  printf("root=%d  ",root);
            break;
        }
    }
    LCA(root);
    //  for(int i=1;i<=n;i++)printf("%d  ",dis[i]); printf("\n");
    for (int i = 0; i < Q; i++)
    {
        len = dis[query[i << 1].q] + dis[query[i << 1 | 1].q] - dis[ans[i]] * 2;
        printf("%d\n", len);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值