[bzoj1787][Ahoi2008]紧急集合

Description

给定一棵大小为的树,有组询问,每组询问给三个点,求到这三个点距离和最小的点及最小距离和.

Input

第一行两个数.

接下来行,每行两个数表示有一条边.

最后行,每行个数,为一组询问.

Output

一共行,每行两个数,表示到三个点距离和最小的点及最小距离和.

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

HINT

Solution

个点两两求,只会有种情况:

1.均相同;

2.有与其他不同.

如果均相同,为答案,否则为与其他不同的那个.

(画图简单推推即可理解)

距离可用深度求出.

#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define K 20
#define N 500005
using namespace std;
struct graph{
    int nxt,to;
}e[N<<1];
int f[N][K],g[N],dep[N],a,b,c,n,m,x,y,z,cnt;
stack<int> s;
inline int read(){
    int ret=0;char c=getchar();
    while(!isdigit(c))
        c=getchar();
    while(isdigit(c)){
        ret=(ret<<1)+(ret<<3)+c-‘0‘;
        c=getchar();
    }
    return ret;
}
inline void addedge(int x,int y){
    e[++cnt].nxt=g[x];g[x]=cnt;e[cnt].to=y;
}
inline void dfs(int u){
    dep[u]=1;s.push(u);
    while(!s.empty()){
        u=s.top();s.pop();
        if(u==1) for(int i=0;i<K;++i)
            f[u][i]=1;
        else for(int i=1;i<K;++i)
            f[u][i]=f[f[u][i-1]][i-1];
        for(int i=g[u];i;i=e[i].nxt)
            if(!dep[e[i].to]){
                dep[e[i].to]=dep[u]+1;
                f[e[i].to][0]=u;
                s.push(e[i].to);
            }
    }
}
inline int swim(int x,int h){
    for(int i=0;h;++i,h>>=1)
        if(h&1) x=f[x][i];
    return x;
}
inline int lca(int x,int y){
    if(dep[x]<dep[y]){
        int t=x;x=y;y=t;
    }
    x=swim(x,dep[x]-dep[y]);
    if(x==y) return x;
    int i;
    while(true){
        for(i=0;f[x][i]!=f[y][i];++i);
        if(!i) return f[x][0];
        x=f[x][i-1];y=f[y][i-1];
    }
}
inline void init(){
    n=read();m=read();
    for(int i=1,j,k;i<n;++i){
        j=read();k=read();
        addedge(j,k);addedge(k,j);
    }
    dfs(1);
    while(m--){
        x=read();y=read();z=read();
        a=lca(x,y);b=lca(y,z);c=lca(x,z);
        if(a==b&&b==c)
            printf("%d %d\n",a,dep[x]+dep[y]+dep[z]-dep[a]*3);
        else if(a==b) printf("%d %d\n",c,dep[x]+dep[y]+dep[z]-dep[c]-(dep[a]<<1));
        else if(a==c) printf("%d %d\n",b,dep[x]+dep[y]+dep[z]-dep[b]-(dep[a]<<1));
        else printf("%d %d\n",a,dep[x]+dep[y]+dep[z]-dep[a]-(dep[b]<<1));
    }
}
int main(){
    freopen("meet.in","r",stdin);
    freopen("meet.out","w",stdout);
    init();
    fclose(stdin);
    fclose(stdout);
    return 0;
}
时间: 2024-10-01 07:12:13

[bzoj1787][Ahoi2008]紧急集合的相关文章

AHOI2008 紧急集合 树上倍增

AHOI2008 紧急集合 题目传送 sol: 如果只有两个点,那么显然目的地就是在他们二者路径上的任意一点. 现在有三个点,考虑两两的路径和lca,发现肯定有两对求得的lca相同,另外一对的lca深度比那两对的lca深度大. 这个深度大一些的那个lca就是目的地(最近点),最小距离就是三者两两距离的二分之一. 所以直接树上倍增即可. #include<bits/stdc++.h> #define IL inline #define RG register #define DB double

BZOJ1787 [Ahoi2008]Meet 紧急集合

水题 求出三个人每两个间的LCA,然后最小花费就是两两点之间的路径长度之和除以2 1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 const int maxn = 500005; 6 struct edge{ 7 int v,next; 8 }e[maxn*2]; 9 struct ask{ 10 int v,next,lca,d,u; 11

[bzoj1787][Ahoi2008]Meet 紧急集合(lca)

传送门 可以看出,三个点两两之间的lca会有一对相同,而另一个lca就是聚集点. 然后搞搞就可以求出距离了. ——代码 1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #define MAXN 1000001 5 6 using namespace std; 7 8 int n, m, cnt, ans; 9 int head[MAXN], to[MAXN], next[MAXN], de

【块状树】【LCA】bzoj1787 [Ahoi2008]Meet 紧急集合

分块LCA什么的,意外地快呢…… 就是对询问的3个点两两求LCA,若其中两组LCA相等,则答案为第三者. 然后用深度减一减什么的就求出距离了. 1 #include<cstdio> 2 #include<algorithm> 3 #include<cmath> 4 using namespace std; 5 #define maxn 500001 6 struct Graph 7 {int v[maxn<<1],first[maxn<<1],n

[AHOI2008] 紧急集合

Description 欢乐岛上有个非常好玩的游戏,叫做"紧急集合".在岛上分散有N个等待点,有N-1条道路连接着它们,每一条道路都连接某两个等待点,且通过这些道路可以走遍所有的等待点,通过道路从一个点到另一个点要花费一个游戏币. 参加游戏的人三人一组,开始的时候,所有人员均任意分散在各个等待点上(每个点同时允许多个人等待),每个人均带有足够多的游戏币(用于支付使用道路的花费).地图(标明等待点之间道路连接的情况)以及对话机(用于和同组的成员联系).当集合号吹响后,每组成员之间迅速联系

[AHOI2008]紧急集合 / 聚会

紧急集合 / 聚会 题目大意: 给出一个无向图,每一次给出图中的三个点,求离三个点距离之和最小的点. 解决方法: 倍增LCA. 首先我们两两点之间求出LCA,那么离他们距离之和最近的点就是三个点中深度最深的点,想一想为什么? 我们假设存在一个点离三个点距离之和更近且深度更浅,那么我们将它的深度往下走一个,一定会有两个点距离-1,一个点+1,所以往下移更靠近正解opt,那么我们就可以得出我们上述结论合法. 而关于距离之和,由于dis(u,v)=dep[u]+dep[v]-dep[lca(u,v)]

luogu P4281 [AHOI2008]紧急集合 / 聚会 |LCA

题目描述 欢乐岛上有个非常好玩的游戏,叫做"紧急集合".在岛上分散有 n 个等待点,有 n?1 条道路连接着它们,每一条道路都连接某两个等待点,且通过这些道路可以走遍所有的等待点,通过道路从一个点到另一个点要花费一个游戏币. 参加游戏的人三人一组,开始的时候,所有人员均任意分散在各个等待点上(每个点同时允许多个人等待),每个人均带有足够多的游戏币(用于支付使用道路的花费).地图(标明等待点之间道路连接的情况)以及对话机(用于和同组的成员联系).当集合号吹响后,每组成员之间迅速联系,了解

BZOJ 1787 AHOI2008 紧急集合 倍增LCA

题目大意:给定一棵树,多次询问到三个点距离之和最小的点和距离 首先易知到两个点距离之和最小的点一定在两点间的路径上 于是到三个点距离之和最小的点一定在两两之间路径的交点上 然后很容易就会知道这个交点一定是其中两个点的LCA(其实是我不会证) 此外为什么不会是三个点共同的LCA呢?因为三个点共同的LCA一定是至少一对点的LCA 证明略(其实我也不会证) 然后就是枚举两两之间的LCA 求一下距离 取最小即可 然后就是倍增LCA的问题了 我的倍增LCA怎么又挂了 还能不能写对了0.0 #include

luoguP4281[AHOI2008]紧急集合 / 聚会

最近复习lca,布置的三道题中最后一道就是这个紫题.当时看到是紫题吓了一跳(果然是我太弱了QAQ)然后仔细读了几遍题,发现这就是普通的lca嘛 (太水了) 其中变化的只有要分别求三者的lca. #include<cstdio> #include<algorithm> #include<iostream> #include<cstring> using namespace std; typedef long long ll; struct kh{ int t,