树上倍增 x
树上倍增.
dfs序的做法:
思路:
//f[i][j]表示dfs序中序号为i到序号为j的点之间深度最小的点的编号
dfs序[]//存0-...(id)编号
节点[]//存dfs序中所经过的节点号
dep[]//存走过的点所对应的点的深度
i : ......
id[i] : (该点在节点编号中第一次出现的位置所对应的dfs序编号)
//ans表示求u节点到i节点的最短距离...
ans=f[id[u]][id[v]];
至于代码……额……没写
树上倍增还能够用来求LCA
其中f[i,j]表示i的第2^j祖先dfs预处理f[i,j]=f[f[i,j-1],j-1];
对于每一对x,y先将深度调成一样再枚举j逐一往上找,这两个过程都是log的
代码如下:
#include <iostream>
#include <cstdio>
#include <cmath>
//maybe my English is not very good using namespace std; const int M = 5e5 + ;
int n,m,s;
int num;
int deep[M],h[M];
bool vs[M];
int jumps[M][];
int p; struct A{
int next;
int to;
}t[M<<]; inline int read() //optimize
{
int x=,f=;char ch=getchar(); while(ch<''||ch>'')
{
if(ch=='-') f=-;
ch=getchar();
} while(ch>=''&&ch<='')
{
x=x*+ch-'';
ch=getchar();
} return x*f;
} void ADD(int x,int y) //connect the x and the y
{
num++;
t[num].to=y;
t[num].next=h[x];
h[x]=num;
} void Dfs(int u)
{
for(int i=h[u];i!=-;i=t[i].next)
{
int v=t[i].to; //u's next side
if(deep[v] == ) //if v is not visited
{
deep[v]=deep[u]+; //deep+1
jumps[v][]=u; //u is v's dad
Dfs(v); //continue Dfs
}
}
} void steps()
{
p=int(log(n)/log()+0.001); //find the biggest
for(int i=;i<=p;i++) //the Limit
for(int j=;j<=n;j++)
jumps[j][i]=jumps[jumps[j][i-]][i-];
//the j jump 2^i can get to the (first jump 2^(i-1),then jump 2^i-1 can get to)
//eh...I will speak in Chinese.
//because 倍增 is use 次方的形式 increase
} int LCA(int a,int b)
{
//We let the b's deep is small
if(deep[a]<deep[b]) swap(a,b);
for(int i=p;i>=;i--)
{//first let the a jump to the b's deep
if(deep[jumps[a][i]]>=deep[b])
a=jumps[a][i];
}
if(a == b) return b; //if the b is them's LCA , return b
for(int i=p;i>=;i--) //jump together
{
if(jumps[a][i]!=jumps[b][i])
a=jumps[a][i],b=jumps[b][i]; //update
}
return jumps[a][];
} int main()
{
//s is the root
n=read();m=read();s=read();
for(int i=;i<=n;i++) h[i]=-;
int x,y;
for(int i=;i<n;i++)
{
x=read();y=read();
//connect the x and the y
ADD(x,y);
ADD(y,x);
}
deep[s]=; //this is too important !!!
//if you don't think so ,"//" it.
//and then you will know
Dfs(s); //Dfs the root(s)
steps(); //find the steps
int a,b;
while(m--)
{
a=read();b=read();
printf("%d\n",LCA(a,b));
}
return ;
} 树上倍增英文版???
1
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<stdio.h>
#include<vector>
#define maxn 500500
using namespace std;
///隶属邻接表
struct Edge{ //邻接表的结构体
int from,to;
}edges[*maxn]; //边要乘2,因为是无向图 ;
int first[maxn],next[*maxn]; //同理;
int read(){ //读入优化,可以照着这个模板来写,这个还算写的比较好看。
int re=;
char ch=getchar();
while (ch<'' || ch>'') ch=getchar();
while (ch>='' && ch<=''){
re=re*+ch-'';
ch=getchar();
}
return re;
}
///////////////////////////////////////////////
///全局变量
int n,m;
int root;
int height[maxn];
float log2n;
///////////////////////////////////////////////////////
///隶属LCA的全局变量
int f[maxn][];//
int have[maxn]; //have,有没有找过,这都是套路 。
void dfs(int u,int h){ //u代表点的标号,h代表高度。
int v;
height[u]=h;
for(int i=;i<=log2n;i++) {
if(h<=(<<i)) break; //由于i是从小到大计算的,故(1<<i)>=h 时可直接退出。请务必想清楚是<= 还是=。
f[u][i] = f[ f[u][i-] ][i-]; //动规计算。同样也是一切倍增算法的核心。
}
int k=first[u];
while(k!=-){
v=edges[k].to;
if(!have[v]) {
have[v]=;
f[v][]=u; //将要找的下一个点的父节点标为当前处理的节点u。
dfs(v,h+);
}
k=next[k];
}
}
int require_LCA(int a,int b){
int da=height[a],db=height[b];
//第一步,将a,b两点移到同样的高度,只动高度大的那个点而不动高度小的那个点。
if(da!=db) {
if(da<db){ //保证a的高度是大于b的高度的。
swap(a,b);
swap(da,db);
}
int d=da-db;
for(int i=;i<=log2n;i++)
if( (<<i) & d) a=f[a][i]; //这里的位运算可以减少代码量
//考虑到d是一个定值,而(1<<i)在二进制中只有第(i+1)位是1;
//那么d与(1<<i)如果某一位为1,那么表示可以向上移动,
//如果此时不移动,那么i增大了后就无法使height[a]==height[b]了
}
//第二步,找到某个位置i,在这个位置时,f[a][i]!=f[b][i],但再向上移动一步,a,b相同了
//从log2n开始从大到小枚举i,如果超过了a,b的高度,则令i继续减小
//如果没有超过a,b的高度,那么就判断移动了后会不会让a==b,
//是,则i继续减小,否则,令此时的a=f[a][i],b=f[b][i];
if(a==b) return b;
int i=;
for(i=log2n;i>=;i--) {
if(height[ f[a][i] ]<) continue;
if( f[a][i]==f[b][i] ) continue;
else a=f[a][i],b=f[b][i]; //顺便一提,在第二步任何地方没有break;
//我就是因为在这里写了一个break,然后找了我两个小时啊。
}
return f[a][];
}
/////////////////////////////////
///据说从主函数开始阅读是个好习惯。
int main(){
// freopen("in2.txt","r",stdin);
n=read();m=read();root=read();
memset(first,-,sizeof(first));
memset(next,-,sizeof(next));
int s,t;
int dsd=*(n-);
for(int i=;i<=dsd;i+=) {
s=read();t=read(); //读入优化。
edges[i].from=s;
edges[i].to=t;
edges[i+].from=t;
edges[i+].to=s;
next[i]=first[s];
first[s]=i;
next[i+]=first[t];
first[t]=i+;
}
// 以上是邻接表,在此不再赘述。
log2n=log(n)/log()+; //C++计算log是自然对数,我们要用的以2为底的对数,故要除以log(2);
//对无理数加上1或是0.5是个好习惯,可以减小误差;
memset(have,,sizeof(have));
memset(height,,sizeof(height));
memset(f,-,sizeof(f));
have[root]=; //fa[][]和height[]要在dfs理进行计算,不然根本找不到某个非根节点的父亲是谁;
dfs(root,);
for(int i=;i<=n;i++){
for(int j=;j<=log2n;j++) {
if(height[i] <=(<<j) ) break;
}
}
for(int i=;i<m;i++) { //应对要求进行求解。
s=read();t=read();
int y=require_LCA(s,t);
printf("%d\n",y);
}
return ;
}
2
End.
树上倍增 x的更多相关文章
- Codevs 2370 小机房的树 LCA 树上倍增
题目描述 Description 小机房有棵焕狗种的树,树上有N个节点,节点标号为0到N-1,有两只虫子名叫飘狗和大吉狗,分居在两个不同的节点上.有一天,他们想爬到一个节点上去搞基,但是作为两只虫子, ...
- NOIP2013 货车运输 (最大生成树+树上倍增LCA)
死磕一道题,中间发现倍增还是掌握的不熟 ,而且深刻理解:SB错误毁一生,憋了近2个小时才调对,不过还好一遍AC省了更多的事,不然我一定会疯掉的... 3287 货车运输 2013年NOIP全国联赛提高 ...
- HDU 4822 Tri-war(LCA树上倍增)(2013 Asia Regional Changchun)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4822 Problem Description Three countries, Red, Yellow ...
- [NOIP2013/Codevs3287]货车运输-最小[大]生成树-树上倍增
Problem 树上倍增 题目大意 给出一个图,给出若干个点对u,v,求u,v的一条路径,该路径上最小的边权值最大. Solution 看到这个题第一反应是图论.. 然而,任意路径最小的边权值最大,如 ...
- 树上倍增求LCA及例题
先瞎扯几句 树上倍增的经典应用是求两个节点的LCA 当然它的作用不仅限于求LCA,还可以维护节点的很多信息 求LCA的方法除了倍增之外,还有树链剖分.离线tarjan ,这两种日后再讲(众人:其实是你 ...
- [树上倍增+二分答案][NOIP2012]运输计划
题目背景 公元 2044 年,人类进入了宇宙纪元. 题目描述 公元 2044 年,人类进入了宇宙纪元 L 国有 nn 个星球,还有 n-1n−1 条双向航道,每条航道建立在两个星球之间,这 n-1n− ...
- 两种lca的求法:树上倍增,tarjan
第一种:树上倍增 f[x,k]表示x的2^k辈祖先,即x向根结点走2^k步达到的结点. 初始条件:f[x][0]=fa[x] 递推式:f[x][k]=f[ f[x][k-1] ][k-1] 一次bfs ...
- 【NOIP2013/Codevs3287】货车运输-最小生成树(大)-树上倍增
https://www.luogu.org/problemnew/show/P1967 由题可知,我们走的路的边应尽可能大,所以通过kruscal建最大生成树的图,再树上倍增,注意可能有多棵树; #i ...
- LCA树上倍增
LCA就是最近公共祖先,比如 节点10和11的LCA就是8,9和3的LCA就是3. 我们这里讲一下用树上倍增来求LCA. 大家都可以写出暴力解法,两个节点依次一步一步往上爬,直到爬到了相同的一个节点. ...
- BZOJ3545&3551[ONTAK2010]Peaks——kruskal重构树+主席树+dfs序+树上倍增
题目描述 在Bytemountains有N座山峰,每座山峰有他的高度h_i.有些山峰之间有双向道路相连,共M条路径,每条路径有一个困难值,这个值越大表示越难走,现在有Q组询问,每组询问询问从点v开始只 ...
随机推荐
- 阶段1 语言基础+高级_1-3-Java语言高级_03-常用API第二部分_第3节 Calendar类_1_Calendar类介绍_获取对象的方式
Calendar.getInstance()返回的就是Calendar的子类对象.GregorianCalendar 说明Calendar类重写了toSting的方法 时区亚洲的上海
- 阶段1 语言基础+高级_1-3-Java语言高级_04-集合_08 Map集合_1_Map集合概述
map集合是双列集合 map有两个泛型.左边K也叫作键 右边V是value
- spring boot配置项profiles active
结论:通用项配置在applicaton.yml,区别环境配置在application-{profile}.yml中 一直不知道这个参数要不要配,配了有什么用,今天搭一个工程来检验 此项作用:用来区分不 ...
- 类File
* File类常用的构造方法: * (1)File(String s);//由s确定File对象的文件名 * (2)File(String directory,String s);//由directo ...
- 06 使用bbed提交delete的数据--01
使用bbed模拟delete提交操作 --session 1 TEST@ orcl )); Table created. TEST@ orcl ,'AAAAA'); row created. TEST ...
- JS-在本页面禁止页面返回
这个问题是最近遇到的 解决方案我百度的并测试有效 // 如果你希望用户不用有返回功能 可缩写如下 或使用location.replace('url')跳转链接 history.pushState(nu ...
- JDK和SDK的区别:
参考链接:https://www.cnblogs.com/vaelailai/p/7976158.html jdk,是Java开发工具包,主要用于编写Java程序:也就是说你要使用Java语言,就需要 ...
- 【AndroidFramework】ATV9遥控器红外模式下,机顶盒在假待机阶段会响应遥控器语音键
[问题描述] 测试部反馈,红外模式下,按power键进入假待机,按红外语音键会唤醒. 背景交代:红外语言键是我们自定义的按键,键值225.在红外模式下按会弹提示框"没连蓝牙,请连蓝牙使用语音 ...
- 【ABAP系列】SAP ABAP ALV里日期类型的F4帮助
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[ABAP系列]SAP ABAP ALV里日期类 ...
- 阻塞队列 BlockingQueue 常用方法详解
1.offer()和add()的区别 add()和offer()都是向队列中添加一个元素.但是如果想在一个满的队列中加入一个新元素,调用 add() 方法就会抛出一个 unchecked 异常,而调用 ...