【AHOI2008】 聚会 倍增

BZOJ
Description
Y岛风景美丽宜人,气候温和,物产丰富。Y岛上有N个城市,有N-1条城市间的道路连接着它们。每一条道路都连接某两个城市。幸运的是,小可可通过这些道路可以走遍Y岛的所有城市。神奇的是,乘车经过每条道路所需要的费用都是一样的。小可可,小卡卡和小YY经常想聚会,每次聚会,他们都会选择一个城市,使得3个人到达这个城市的总费用最小。 由于他们计划中还会有很多次聚会,每次都选择一个地点是很烦人的事情,所以他们决定把这件事情交给你来完成。他们会提供给你地图以及若干次聚会前他们所处的位置,希望你为他们的每一次聚会选择一个合适的地点。
Input

第一行两个正整数,N和M。分别表示城市个数和聚会次数。后面有N-1行,每行用两个正整数A和B表示编号为A和编号为B的城市之间有一条路。城市的编号是从1到N的。再后面有M行,每行用三个正整数表示一次聚会的情况:小可可所在的城市编号,小卡卡所在的城市编号以及小YY所在的城市编号。
Output

一共有M行,每行两个数Pos和Cost,用一个空格隔开。表示第i次聚会的地点选择在编号为Pos的城市,总共的费用是经过Cost条道路所花费的费用。

Sample Input

6 4

1 2

2 3

2 4

4 5

5 6

4 5 6

6 3 1

2 4 4

6 6 6

Sample Output

5 2

2 5

4 1

6 0

数据范围:

100%的数据中,N<=500000,M<=500000。

40%的数据中N<=2000,M<=2000。

题解:
没什么特别好的思路,感觉我就是暴力。。。
嗯,就是把三个点两两的LCA = t找出来,然后找出t与第三个点的LCA,然后计算长度,嗯,三个长度都算出来了,之后比较三个长度的长短,输出最短的。就算用的倍增找LCA,而且思路感觉有点偷懒,最后还是成功AC,且在BZOJ排在100名,还可以。
代码:

#include <iostream>
#include <cstdio>
using namespace std;

const int MAXN = 500005;

int N,M,MAXD,e = 1;
int head[MAXN],deep[MAXN],anc[MAXN][25];

inline int abs(int x){ return x > 0 ? x : -x; }

struct node{
    int v,next;
}edge[MAXN*2];

inline void addedge(int u,int v){
    edge[e] = (node){v,head[u]};head[u] = e++;
    edge[e] = (node){u,head[v]};head[v] = e++;
}

inline int read(){
    int x = 0;char ch = getchar();
    while(ch < '0' || '9' < ch) {ch = getchar();}
    while('0' <= ch&& ch <='9') {x = x * 10 +ch-'0';ch = getchar();}
    return x;
}

inline void init(){
    N = read();M = read();

    for(int i = 1;i < N;i <<= 1,MAXD++);

    int u,v;

    for(int i = 1;i < N;i++){
        u = read();v = read();
        addedge(u,v);
    }
}

void dfs(int u,int h){
    deep[u] = h;

    for(int i = 1;i <= MAXD;i++) anc[u][i] = anc[ anc[u][i-1] ][i-1];

    for(int i = head[u];i;i = edge[i].next){
        int v = edge[i].v;
        if(!deep[v]){
            anc[v][0] = u;
            dfs(v,h+1);
        }
    }
}

inline void swim(int &u,int h){
    for(int i = 0;h;i++){
        if(h&1) u = anc[u][i];
        h >>= 1;
    }
}

inline int query(int u,int v){
    if(deep[u] < deep[v]) swap(u,v);

    swim(u,deep[u] - deep[v]);

    if(u == v) return u;

    for(int i = MAXD;i >= 0;i--)
       if(anc[u][i] != anc[v][i]){
        u = anc[u][i];
        v = anc[v][i];
    }
    if(u != v) return anc[u][0];
    return u;
}

int main(){
    init();
    dfs(1,1);
    int a,b,c,lca_a_b,lca_b_c,lca_a_c,lca_1,lca_2,lca_3,dist1,dist2,dist3;
    for(int i = 1;i <= M;i++)
    {

        a = read();b = read();c = read();

        lca_a_b = query(a,b);lca_b_c = query(b,c);lca_a_c = query(a,c);

        lca_1 = query(lca_a_b,c);lca_2 = query(lca_b_c,a);lca_3 = query(lca_a_c,b);

        dist1 = abs(deep[lca_a_b] - deep[a]) + abs(deep[lca_a_b] - deep[b]) + abs(deep[lca_1] - deep[c]) + abs(deep[lca_1] - deep[lca_a_b]);
        dist2 = abs(deep[lca_b_c] - deep[b]) + abs(deep[lca_b_c] - deep[c]) + abs(deep[lca_2] - deep[a]) + abs(deep[lca_1] - deep[lca_b_c]);
        dist3 = abs(deep[lca_a_c] - deep[a]) + abs(deep[lca_a_c] - deep[c]) + abs(deep[lca_3] - deep[b]) + abs(deep[lca_1] - deep[lca_a_c]);

        if(dist1 > dist2){
            if(dist2 > dist3) printf("%d %d\n",lca_a_c,dist3);
            else printf("%d %d\n",lca_b_c,dist2);
        }

        else {
            if(dist1 > dist3) printf("%d %d\n",lca_a_c,dist3);
            else printf("%d %d\n",lca_a_b,dist1);
        }
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值