[AHOI2008]紧急集合 / 聚会

紧急集合 / 聚会

题目大意:

给出一个无向图,每一次给出图中的三个点,求离三个点距离之和最小的点。

解决方法:

倍增LCA。

首先我们两两点之间求出LCA,那么离他们距离之和最近的点就是三个点中深度最深的点,想一想为什么?

我们假设存在一个点离三个点距离之和更近且深度更浅,那么我们将它的深度往下走一个,一定会有两个点距离-1,一个点+1,所以往下移更靠近正解opt,那么我们就可以得出我们上述结论合法。

而关于距离之和,由于dis(u,v)=dep[u]+dep[v]-dep[lca(u,v)],那么我们把三个点两两之间∑一下再除以二就能得到答案了,式子就不推了。。。

dis=dep[x]+dep[y]+dep[z]-dep[lca(x,y)]-dis[lca(y,z)]-dis[lca(x,z)]

最后附上本题代码:

  1 #include<cstdio>
  2 #include<iostream>
  3 #define maxn 500000
  4 using namespace std;
  5
  6 int n,m,root,cnt;
  7 int head[maxn+5],dep[maxn+5],f[maxn+5][30];
  8 bool vis[maxn+5];
  9 struct EDGE
 10 {
 11     int nxt,to;
 12 };
 13 EDGE edge[maxn*2+5];
 14
 15 int max(int x,int y)
 16 {
 17     if(dep[x]>=dep[y])
 18     {
 19         return x;
 20     }
 21     return y;
 22 }
 23 int abs(int x)
 24 {
 25     if(x<0)
 26     {
 27         return -x;
 28     }
 29     return x;
 30 }
 31 void add(int x,int y)
 32 {
 33     edge[++cnt].to=y;
 34     edge[cnt].nxt=head[x];
 35     head[x]=cnt;
 36 }
 37 void pre_fir(int u,int fa)
 38 {
 39     dep[u]=dep[fa]+1;
 40     for(int i=0;i<=25;i++)
 41     {
 42         f[u][i+1]=f[f[u][i]][i];
 43     }
 44     for(int i=head[u];i;i=edge[i].nxt)
 45     {
 46         if(edge[i].to==fa)
 47         {
 48             continue;
 49         }
 50         f[edge[i].to][0]=u;
 51         pre_fir(edge[i].to,u);
 52     }
 53 }
 54 int LCA(int x,int y)
 55 {
 56     if(dep[x]<dep[y])
 57     {
 58         swap(x,y);
 59     }
 60     for(int i=25;i>=0;i--)
 61     {
 62         if(dep[f[x][i]]>=dep[y])
 63         {
 64             x=f[x][i];
 65         }
 66         if(x==y)
 67         {
 68             return x;
 69         }
 70     }
 71     for(int i=25;i>=0;i--)
 72     {
 73         if(f[x][i]!=f[y][i])
 74         {
 75             x=f[x][i];
 76             y=f[y][i];
 77         }
 78     }
 79     return f[x][0];
 80 }
 81 int main()
 82 {
 83     scanf("%d%d",&n,&m);
 84     for(int i=1;i<=n-1;i++)
 85     {
 86         int x,y;
 87         scanf("%d%d",&x,&y);
 88         add(x,y);
 89         add(y,x);
 90         vis[y]=1;
 91     }
 92     for(int i=1;i<=n;i++)
 93     {
 94         if(vis[i]==0)
 95         {
 96             root=i;
 97             break;
 98         }
 99     }
100     dep[root]=1;
101     pre_fir(root,0);
102     for(int i=1;i<=m;i++)
103     {
104         int x,y,z;
105         scanf("%d%d%d",&x,&y,&z);
106         int ans1=LCA(x,y),ans2=LCA(y,z),ans3=LCA(x,z);
107         int ans=max(max(ans1,ans2),ans3);
108         printf("%d %d\n",ans,dep[x]+dep[y]+dep[z]-dep[ans1]-dep[ans2]-dep[ans3]);
109     }
110     return 0;
111 }

原文地址:https://www.cnblogs.com/yufenglin/p/10579901.html

时间: 2024-08-25 23:24:08

[AHOI2008]紧急集合 / 聚会的相关文章

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

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

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,

「AHOI2008」「LuoguP4281」紧急集合 / 聚会(LCA

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

AHOI2008 紧急集合 树上倍增

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

[AHOI2008] 紧急集合

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

[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 对

【BZOJ1832】【AHOI2008】聚会 倍增lca

这道题写不了tarjanlca. 50W的询问,也就是150W次lca查询,每次加三条边,内存妥妥要爆. 只能退求logn的倍增lca了. sad story. 还好一遍AC. 题解: 就是发现每次询问三个点之间有唯一的路径集,那么我们选其中两个取lca,然后另一个点自己走到这个lca, 就可以贪心取得答案. 代码: #include <cstdio> #include <cstring> #include <iostream> #include <algorit

BZOJ 1787 AHOI2008 紧急集合 倍增LCA

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

luogu4281

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