1787: [Ahoi2008]Meet 紧急集合

Time Limit: 20 Sec  Memory Limit:162 MB
Submit: 590  Solved: 240
[Submit][Status][Discuss]

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

Source

Day1

解析:裸的LCA(即最近公共祖先)。设a、b、c三点,则集合点必为lca(a,b)、lca(a,c)、lca(b,c)中的一个,然后求解个点到集合点的距离就可以了。

1.如何求解树上的两个点a、b的距离:

用 h[i] 记录节点i 在树中的深度,若 j 为 i 的子节点,则有:h[j]==h[i]+1;

k=lca(a,b),则length=h[a]-h[k]+h[b]-h[k]。

2.如何求解最近公共祖先:

①tarjan。(这个这里不讲,请自行百度学习)

②转化为 rmq 问题求解。

若树的形状为下图所示的二叉树,我们对其进行中序遍历,则有:

中序遍历:4 2 5 1 6 3 7

深度:       3 2 3 1 3 2 3

观察发现:数的父节点的深度总是低于自己节点的;中序遍历序列,父节点总位于两子节序列之间。

==》中序遍历中,a、b的父节点即为[a,b]上,深度h最小的点。

于是,求解LCA的问题就转化为求解一段序列的最小值,也即 rmq 问题。

这里只罗列两种解决rmq问题的方法:

第一个:线段树,这个就不多讲了。

第二个:st算法。

我们用a[i][j]便是以 i 为起点,长度为 2^j 的序列上的最小值。

a[i][j]=min(a[i][j-1],a[i+2^(j-1)][j-1]);

查询区间[l,r]的最小值:

i= r - l ,j=(int)(log(j*1.0)/log(2.0)),k=2^j

区间最小值为:min(a[i][j],a[r-k+1][j])。

st与线段树相比,空间消耗是一样的,但是线段树的查询是o(log n),而st的查询是o(1)的。

那现在如果不是二叉树呢?

只要保证生成序列中,每两个子序列之间有一个父节点见他们隔开即可,比如:

遍历序列:4 2 5 1 8 1 6 3 7

我在程序里,为了写的方便,生成的序列是用两个父节点包围一个子序列,即:

1 (2 4 2 5 2) 1 8 1 (3 6 3 7 3) 1

③倍增。

这可谓是处理树形数据的常用方法之一。

up[i][j]表示节点沿着树上的路径,向上走2^j 步所能到达的点,则:

up[i][j]=up[ up[i][j-1] ] [ j-1 ]

其他的具体应用,就去看下方的代码吧。

rmq-st:

以 1 作为根节点建树,h[i]记录点 i 在书中的深度,i的子节点j的深度即为:h[j]=h[i]+1;
b[i]记录 i 在生成序列中的位置(我这里取最后一次出现的地方,其实随便哪里都可以)
a[i][j]记录序列中从 i 开始,长度为2^j的子序列中,h值最小的点,也就是这一子区间内的最近公共祖先 
d[i]=2^i
head[]用来存储变边

<span style="font-size:18px;">
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;const int maxn=5e5;
int sum,h[maxn+10];
int a[maxn*2+10][21];
int b[maxn+10];
int d[30];
struct tnode{int x;tnode *next;
}*head[maxn+10];void add_edge(int x,int y)
{tnode *p=new tnode;(*p).x=y,(*p).next=head[x];head[x]=p;
}void build_queue(int x)
{tnode *p;int k;a[++sum][0]=x,b[x]=sum;for(p=head[x];p;p=(*p).next){if(h[(k=(*p).x)])continue;h[k]=h[x]+1,build_queue(k);a[++sum][0]=x,b[x]=sum;}
}int get_father(int x,int y)
{int i,j,k,s=b[x],t=b[y];if(s>t)swap(s,t);k=(int)(log(t-s+1.0)/log(2.0));i=a[s][k],j=a[t-d[k]+1][k];return (h[i]<h[j])?i:j;
}int get_length(int k,int x,int y,int z)
{int ans=h[x]+h[y]-2*h[k];int i=get_father(k,z);ans+=h[z]+h[k]-2*h[i];return ans;
}int main()
{int n,m,i,j,k,x,y,z,xy,xz,yz,ans1,ans2;scanf("%d%d",&n,&m);for(i=1;i<=n;i++)h[i]=NULL;for(d[0]=1,i=1;i<=19;i++)d[i]=d[i-1]*2;for(i=1;i<n;i++){scanf("%d%d",&x,&y);add_edge(x,y),add_edge(y,x);}h[1]=1,sum=0,build_queue(1);for(j=1;j<=19;j++)for(i=1;i+d[j]-1<=sum;i++)if(h[a[i][j-1]]<h[a[i+d[j-1]][j-1]])a[i][j]=a[i][j-1];else a[i][j]=a[i+d[j-1]][j-1];for(i=1;i<=m;i++){scanf("%d%d%d",&x,&y,&z);ans1=get_father(x,y);ans2=get_length(ans1,x,y,z);k=get_father(x,z),j=get_length(k,x,z,y);if(j<ans2)ans1=k,ans2=j;k=get_father(y,z),j=get_length(k,y,z,x);if(j<ans2)ans1=k,ans2=j;printf("%d %d\n",ans1,ans2);}return 0;
}</span>

倍增:

h[i]:记录点i在树中的深度,子节点j的深度为:h[j]=h[i]+1
up[i][j]:记录点i向上走2^i步所到达的点,up[i][j]=up[up[i][j-1]][j-1]
head[]:记录树中的边

<span style="font-size:18px;">#include<cstdio>
#include<algorithm>
using namespace std;const int maxn=5e5;
int up[maxn+10][20];
int h[maxn+10];
struct tnode{int x;tnode *next;
}*head[maxn+10]; void add_edge(int x,int y)
{tnode *p=new tnode;(*p).x=y,(*p).next=head[x],head[x]=p;
}void build_tree(int x)
{tnode *p;int i,k;for(p=head[x];p;p=(*p).next){if(h[k=(*p).x])continue; h[k]=h[x]+1,up[k][0]=x;for(i=1;i<=18;i++)if(!(up[k][i]=up[up[k][i-1]][i-1]))break;build_tree(k);  }
}int get_father(int x,int y)
{if(h[x]<h[y])swap(x,y);int i,len=h[x]-h[y];for(i=0;i<=18;i++)if((1<<i)&len)x=up[x][i];for(i=18;i>=0;i--)if(up[x][i]!=up[y][i])x=up[x][i],y=up[y][i];if(x==y)return x;return up[x][0];
}int get_length(int k,int x,int y,int z)
{int ans=h[x]+h[y]-2*h[k];int i=get_father(k,z);return ans+=h[k]+h[z]-2*h[i];
}int main()
{int n,m,i,j,k,x,y,z,ans1,ans2;scanf("%d%d",&n,&m);for(i=1;i<=n;i++)head[i]=NULL,h[i]=0;for(i=1;i<n;i++){scanf("%d%d",&x,&y);add_edge(x,y),add_edge(y,x);}h[1]=1,up[1][0]=0,build_tree(1);for(k=1;k<=m;k++){scanf("%d%d%d",&x,&y,&z);ans1=get_father(x,y),ans2=get_length(ans1,x,y,z);i=get_father(x,z),j=get_length(i,x,z,y);if(j<ans2)ans1=i,ans2=j;i=get_father(y,z),j=get_length(i,y,z,x);if(j<ans2)ans1=i,ans2=j;printf("%d %d\n",ans1,ans2);}return 0;
}</span>

[Ahoi2008] Meet 紧急集合 (LCA+倍增+rmq-st)相关推荐

  1. BZOJ 1787 [Ahoi2008]Meet 紧急集合——LCA

    1787: [Ahoi2008]Meet 紧急集合 题目传送门 解题思路 本题是裸的LCA,特殊就在是三个点的LCA,然而并没有什么关系. 三个节点两两求LCA,将会有两个一样的公共祖先,剩下的一个就 ...

  2. bzoj 1787 bzoj 1832: [Ahoi2008]Meet 紧急集合(倍增LCA)

    1832: [AHOI2008]聚会 Time Limit: 10 Sec  Memory Limit: 64 MB Submit: 1539  Solved: 663 [Submit][Status ...

  3. BZOJ1787 [Ahoi2008]Meet 紧急集合 LCA

    欢迎访问~原文出处--博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1787 题意概括 有一棵节点为n个(n≤500000)的树.接下来m次询问(m≤500000),每次 ...

  4. [bzoj1787][Ahoi2008]Meet 紧急集合 倍增LCA

    1787: [Ahoi2008]Meet 紧急集合 Time Limit: 20 Sec Memory Limit: 162 MB Submit: 999999999 Solved: 99999999 ...

  5. [Ahoi2008]Meet 紧急集合

    1787: [Ahoi2008]Meet 紧急集合 Time Limit: 20 Sec  Memory Limit: 162 MB http://www.lydsy.com/JudgeOnline/ ...

  6. 1787: [Ahoi2008]Meet 紧急集合

    1787: [Ahoi2008]Meet 紧急集合 Time Limit: 20 Sec  Memory Limit: 162 MB Submit: 1482  Solved: 652 [Submit ...

  7. bzoj1787 [Ahoi2008]Meet 紧急集合

    1787: [Ahoi2008]Meet 紧急集合 Time Limit: 20 Sec   Memory Limit: 162 MB Submit: 2272   Solved: 1029 [ Su ...

  8. BZOJ 1787: [Ahoi2008]Meet 紧急集合

    BZOJ 1787: [Ahoi2008]Meet 紧急集合 题目描述 欢乐岛上有个非常好玩的游戏,叫做"紧急集合".在岛上分散有N个等待点,有N-1条道路连接着它们,每一条道路都 ...

  9. BZOJ 1787 [Ahoi2008]Meet紧急集合 题解与分析

    [Ahoi2008]Meet 紧急集合 Time Limit: 20 Sec   Memory Limit: 162 MB Description Input Output Sample Input ...

最新文章

  1. 单细胞RNA降维之UMAP
  2. git的一些常用命令
  3. Docker 修改镜像源地址
  4. 【IT资讯】Linus Torvalds:我们都老了,但Linux维护真的很难找
  5. Redis java API ——Jedis的使用
  6. 快手基于 Apache Flink 的优化实践
  7. python实现的json数据以HTTP GET,POST,PUT,DELETE方式页面请求
  8. hdu 3105 Fred's Lotto Tickets (水)
  9. Module-Zero之组织单元(OU)管理【新增】
  10. 数据结构笔记(六)-- 双向链表
  11. 官方实力榜:绿军居首黄蜂第二 火箭小降雄鹿飙升
  12. Codeforces 208A:Dubstep(字符串)
  13. 【基于JavaEE的医院药品管理系统的设计与实现】
  14. iOS8过渡到iOS9,Xcode6过渡到Xcode7
  15. vim:the damn garbled of vim-devicons from nerdtree
  16. 短信验证php_php如何实现短信验证
  17. android12.0(S) 从SD卡导入vCard文件到通讯录 号码带“-“ 如何把横线去除
  18. zcmu-1919: kirito(多重背包——二进制优化)
  19. Scratch软件编程等级考试四级——20201219
  20. 使用table2excel做excel表格下载

热门文章

  1. android okgo参数,android okgo post传数组
  2. 组合数学之递推关系(二)常系数线性齐次递推关系及其通项求解
  3. 工程职业伦理_Mooc_2019_期末考试参考答案
  4. 股票金融K线图控件AnyStock详细介绍教程
  5. mediaplayer android mp3 url,Android MediaPlayer 播放音频
  6. MT40A1G8SA-062E AAT:E内存颗粒D9XSP芯片
  7. opencv图像处理初步(一):灰度化和二值化
  8. poj1061 青蛙的约会(扩展欧几里德)
  9. C#小游戏——贪吃蛇~详细过程+全部代码
  10. delphi RichEdit控件中插入GIF动画表情