[AHOI2008] 紧急集合
Description
欢乐岛上有个非常好玩的游戏,叫做“紧急集合”。在岛上分散有N个等待点,有N-1条道路连接着它们,每一条道路都连接某两个等待点,且通过这些道路可以走遍所有的等待点,通过道路从一个点到另一个点要花费一个游戏币。
参加游戏的人三人一组,开始的时候,所有人员均任意分散在各个等待点上(每个点同时允许多个人等待),每个人均带有足够多的游戏币(用于支付使用道路的花费)、地图(标明等待点之间道路连接的情况)以及对话机(用于和同组的成员联系)。当集合号吹响后,每组成员之间迅速联系,了解到自己组所有成员所在的等待点后,迅速在N个等待点中确定一个集结点,组内所有成员将在该集合点集合,集合所用花费最少的组将是游戏的赢家。
小可可和他的朋友邀请你一起参加这个游戏,由你来选择集合点,聪明的你能够完成这个任务,帮助小可可赢得游戏吗?
Input
第一行两个正整数N和M(N<=500000,M<=500000),之间用一个空格隔开。分别表示等待点的个数(等待点也从1到N进行编号)和获奖所需要完成集合的次数。 随后有N-1行,每行用两个正整数A和B,之间用一个空格隔开,表示编号为A和编号为B的等待点之间有一条路。 接着还有M行,每行用三个正整数表示某次集合前小可可、小可可的朋友以及你所在等待点的编号。
Output
一共有M行,每行两个数P,C,用一个空格隔开。其中第i行表示第i次集合点选择在编号为P的等待点,集合总共的花费是C个游戏币。
说明
\(100\%\)的数据中,\(N\leq500000\),\(M\leq500000\)
Solution
这题就差在说明里写上“这是一道性质题,推出来性质你就能A,不然就乖乖打暴力吧!”
首先能观察到的是这三个点之间两两的 \(lca\) 只能是两个点或更少
也就是说,必定有至少两对点是同一个 \(lca\)
这启发我们从 \(lca\) 入手推性质。
手玩几组数据发现答案就是三个 \(lca\) 中深度较浅的那个。
所以直接求出这三个 \(lca\) 然后暴力求距离即可
但是正解好像是再推一下式子,发现无论如何 $$ans=dep[x]+dep[y]+dep[z]-dep[lca(x,y)]-dep[lca(x,z)]-dep[lca(y,z)]$$.
求距离都不用,直接减就行了。
#include<cstdio>
#include<cctype>
#define N 500005
#define min(A,B) ((A)<(B)?(A):(B))
#define swap(A,B) ((A)^=(B)^=(A)^=(B))
int dfn[N],top[N],d[N];
int n,m,cnt,tot,sum[N<<2];
int fa[N],sze[N],son[N],head[N];
struct Edge{
int to,nxt;
}edge[N<<1];
void add(int x,int y){
edge[++cnt].to=y;
edge[cnt].nxt=head[x];
head[x]=cnt;
}
int getint(){
int x=0;char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return x;
}
void first_dfs(int now){
sze[now]=1;
for(int i=head[now];i;i=edge[i].nxt){
int to=edge[i].to;
if(sze[to])
continue;
fa[to]=now;
d[to]=d[now]+1;
first_dfs(to);
sze[now]+=sze[to];
if(sze[to]>sze[son[now]])
son[now]=to;
}
}
void second_dfs(int now,int low){
top[now]=low;
dfn[now]=++tot;
if(son[now])
second_dfs(son[now],low);
for(int i=head[now];i;i=edge[i].nxt){
int to=edge[i].to;
if(to==fa[now] or to==son[now])
continue;
second_dfs(to,to);
}
}
int query(int cur,int l,int r,int ql,int qr){
if(ql<=l and r<=qr)
return r-l+1;
int mid=l+r>>1,ans=0;
if(ql<=mid)
ans+=query(cur<<1,l,mid,ql,qr);
if(mid<qr)
ans+=query(cur<<1|1,mid+1,r,ql,qr);
return ans;
}
int lca(int x,int y){
while(top[x]!=top[y]){
if(d[top[x]]<d[top[y]])
swap(x,y);
x=fa[top[x]];
}
if(d[x]<d[y])
swap(x,y);
return y;
}
int ask(int x,int y){
int ans=0;
while(top[x]!=top[y]){
if(d[top[x]]<d[top[y]])
swap(x,y);
ans+=query(1,1,n,dfn[top[x]],dfn[x]);
x=fa[top[x]];
}
if(d[x]<d[y])
swap(x,y);
if(x!=y)
ans+=query(1,1,n,dfn[y]+1,dfn[x]);
return ans;
}
signed main(){
n=getint(),m=getint();
for(int i=1;i<n;i++){
int x=getint(),y=getint();
add(x,y);add(y,x);
}
d[1]=1; first_dfs(1);
second_dfs(1,1);
while(m--){
int a=getint(),b=getint(),c=getint();
int x=lca(a,b),y=lca(a,c),z=lca(b,c);
int ans=d[a]+d[b]+d[c]-d[x]-d[y]-d[z];
if(d[x]>=d[y] and d[x]>=d[z])
printf("%d %d\n",x,ans);
else if(d[y]>=d[x] and d[y]>=d[z])
printf("%d %d\n",y,ans);
else if(d[z]>=d[x] and d[z]>=d[y])
printf("%d %d\n",z,ans);
/*int a=getint(),b=getint(),c=getint();
int x=lca(a,b);
int y=lca(a,c);
if(x!=y){
if(d[x]<d[y]){
int ans=d[a]+d[c]-2*d[y];
ans+=ask(b,y);
printf("%d %d\n",y,ans);
} else{
int ans=d[a]+d[b]-2*d[x];
ans+=ask(c,x);
printf("%d %d\n",x,ans);
}
} else{
int z=lca(b,c);
if(z==x){
int ans=d[a]+d[b]+d[c]-3*d[z];
printf("%d %d\n",z,ans);
} else{
if(d[x]<d[z]){
int ans=d[b]+d[c]-2*d[z];
ans+=ask(a,z);
printf("%d %d\n",z,ans);
} else{
int ans=d[a]+d[b]-2*d[x];
ans+=ask(c,x);
printf("%d %d\n",x,ans);
}
}
}*/
}
return 0;
}
[AHOI2008] 紧急集合的更多相关文章
- P4281 [AHOI2008]紧急集合 / 聚会
P4281 [AHOI2008]紧急集合 / 聚会 lca 题意:求3个点的lca,以及3个点与lca的距离之和. 性质:设点q1,q2,q3 两点之间的lca t1=lca(q1,q2) t2=lc ...
- [AHOI2008]紧急集合 / 聚会(LCA)
[AHOI2008]紧急集合 / 聚会 题目描述 欢乐岛上有个非常好玩的游戏,叫做"紧急集合".在岛上分散有N个等待点,有N-1条道路连接着它们,每一条道路都连接某两个等待点,且通 ...
- [AHOI2008]紧急集合 / 聚会
题目描述 欢乐岛上有个非常好玩的游戏,叫做“紧急集合”.在岛上分散有N个等待点,有N-1条道路连接着它们,每一条道路都连接某两个等待点,且通过这些道路可以走遍所有的等待点,通过道路从一个点到另一个点要 ...
- LCA【p4281】[AHOI2008]紧急集合 / 聚会
Description 欢乐岛上有个非常好玩的游戏,叫做"紧急集合".在岛上分散有N个等待点,有N-1条道路连接着它们,每一条道路都连接某两个等待点,且通过这些道路可以走遍所有的等 ...
- [bzoj1787][Ahoi2008]紧急集合
Description 给定一棵大小为的树,有组询问,每组询问给三个点,求到这三个点距离和最小的点及最小距离和. Input 第一行两个数. 接下来行,每行两个数表示到有一条边. 最后行,每行个数,为 ...
- 【题解】洛谷P4281 [AHOI2008] 紧急集合(求三个点LCA)
洛谷P4281:https://www.luogu.org/problemnew/show/P4281 思路 答案所在的点必定是三个人所在点之间路径上的一点 本蒟蒻一开始的想法是:先求出2个点之间的L ...
- Luogu 4281 [AHOI2008]紧急集合 / 聚会
BZOJ 1832 写起来很放松的题. 首先发现三个点在树上一共只有$3$种形态,大概长这样: 这种情况下显然走到三个点的$lca$最优. 这种情况下走到中间那个点最优. 这种情况下走到$2$最优. ...
- BZOJ 1832、1787 洛谷 4281 [AHOI2008]紧急集合
[题解] 题目要求找到一个集合点,使3个给定的点到这个集合点的距离和最小,输出集合点的编号以及距离. 设三个点为A,B,C:那么我们可以得到Dis=dep[A]+dep[B]+dep[C]-dep[L ...
- P4281 [AHOI2008]紧急集合 / 聚会[LCA]
解析 蒟蒻用的办法比较蠢,不如上面的各位大佬,直接化成一个式子了,我还是分类讨论做的. 下面正文. 猜想:最优集合点一定是三点任意两对点对应的路径的交点. 不妨这样想,如果任意两个人经过同一条路径,那 ...
随机推荐
- ABP Quartz 作业调度第三篇
1.第一步安装Abp.Quartz ,把他安装到核心层 核心模块添加对quarz的依赖, 领域层创建firstjob类 public class FirstJob : JobBase, ITransi ...
- SPOJ - AMR11B
题目链接:https://www.spoj.com/problems/AMR11B/en/ 题目大意就是要你求图形覆盖的格点数,标记每个图形里的未标记格点(包括边界),总标记数就是覆盖的总格点数. # ...
- SQL笔试基础
SQLSERVER服务器中,给定表table1 中有两个字段 ID.LastUpdateDate,ID表示更新的事务号,LastUpdateDate表示更新时的服务器时间,请使用一句SQL语句获得最后 ...
- jquery如何在元素后面添加一个元素
jQuery添加插入元素技巧: jquery添加分为在指定元素的里面添加和外面添加两种: 里面添加使用(append 和prepend) 里面添加又分为在里面的前面添加和后面添加 里面的前面添加使用 ...
- iOS架构模式浅析
这是以前旧博客在13年规划写的一个系列,写了一部分内容,还没有完成.现在重新整理编写.计划从基础知识六大设计原则.设计模式中类的关系开始,然后会对iOS开发中的常用架构模式进行介绍,最后对GoF的23 ...
- 利用Burp Suite攻击Web应用
i春秋作家:Passerby2 web应用测试综述: Web应用漏洞给企业信息系统造成了很大的风险.许多web应用程序漏洞是由于web应用程序缺乏对输入的过滤.简而言之Web应用程序利用来自用户的某种 ...
- Mac 下 Gradle 环境配置
1. gradle路径的查找 然后gradle 右键 显示简介 复制下蓝色的 2. 环境变量的配置 在.bash_profile文件中,添加如下图选中内容的配置信息: 执行source .bash_p ...
- SpringBoot swagger-ui.html 配置类继承 WebMvcConfigurationSupport 类后 请求404
1 .SpringBoot启动类加上 注解 @EnableWebMvc @SpringBootApplication@EnableWebMvc public class Application { ...
- MongoDB 用法入门(windows)①
概述 大家对数据库肯定不陌生,肯定也有很多人用过MySQL,但是在用MySQL的时候各种建表,写表之间的关联让人非常头疼. MongoDB也是一种数据库,但是它不是用表,而是用集合来装数据的,我对这种 ...
- 自动化测试之数据库操作pymysql
1.下载并导入pymysql 2.配置参数连接mysql db = pymysql.connect(**config) config = { 'host': str(host), 主机地址 'user ...