BZOJ 1787: [Ahoi2008]Meet 紧急集合
题目描述
欢乐岛上有个非常好玩的游戏,叫做“紧急集合”。在岛上分散有N个等待点,有N-1条道路连接着它们,每一条道路都连接某两个等待点,且通过这些道路可以走遍所有的等待点,通过道路从一个点到另一个点要花费一个游戏币。
参加游戏的人三人一组,开始的时候,所有人员均任意分散在各个等待点上(每个点同时允许多个人等待),每个人均带有足够多的游戏币(用于支付使用道路的花费)、地图(标明等待点之间道路连接的情况)以及对话机(用于和同组的成员联系)。当集合号吹响后,每组成员之间迅速联系,了解到自己组所有成员所在的等待点后,迅速在N个等待点中确定一个集结点,组内所有成员将在该集合点集合,集合所用花费最少的组将是游戏的赢家。
小可可和他的朋友邀请你一起参加这个游戏,由你来选择集合点,聪明的你能够完成这个任务,帮助小可可赢得游戏吗?
题解:手画一下,选择三个点两两组成 LCA 中深度最大的即可.
#include<bits/stdc++.h> #define maxn 600000 using namespace std; void setIO(string s) { string in=s+".in"; freopen(in.c_str(),"r",stdin); } int edges,n,Q; int hd[maxn],to[maxn<<1],nex[maxn<<1],dep[maxn],siz[maxn],hson[maxn],fa[maxn],top[maxn]; void add(int u,int v) { nex[++edges]=hd[u],hd[u]=edges,to[edges]=v; } void dfs1(int u,int ff) { fa[u]=ff,dep[u]=dep[ff]+1,siz[u]=1; for(int i=hd[u];i;i=nex[i]) { int v=to[i]; if(v==ff) continue; dfs1(v,u); siz[u]+=siz[v]; if(siz[v]>siz[hson[u]]) hson[u]=v; } } void dfs2(int u,int tp) { top[u]=tp; if(hson[u]) dfs2(hson[u],tp); for(int i=hd[u];i;i=nex[i]) { int v=to[i]; if(v==fa[u]||v==hson[u]) continue; dfs2(v,v); } } int LCA(int x,int y) { while(top[x]^top[y]) dep[top[x]] > dep[top[y]] ? x = fa[top[x]] : y = fa[top[y]]; return dep[x] < dep[y] ? x : y; } int Getdis(int x,int y) { return dep[x] + dep[y] - (dep[LCA(x,y)]<<1); } int main() { // setIO("input"); scanf("%d%d",&n,&Q); for(int i=1,u,v;i<n;++i) { scanf("%d%d",&u,&v); add(u,v), add(v,u); } dfs1(1,0); dfs2(1,1); int u,v,t; while(Q--) { scanf("%d%d%d",&u,&v,&t); int d1=LCA(u,v),d2=LCA(u,t),cur,d3=LCA(v,t); cur=dep[d1]<dep[d2]?d2:d1; cur=dep[cur]<dep[d3]?d3:cur; printf("%d %d\n",cur,Getdis(u,cur)+Getdis(v,cur)+Getdis(t,cur)); } return 0; }