树上倍增 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开始只 ...
随机推荐
- Spring学习01——HelloSpring
这是一个spring入门demo: package com.su.test; public class HelloWorld { public void say(){ System.out.print ...
- Leaflet
https://leafletjs.com/ https://github.com/Leaflet/Leaflet
- PHP多图片上传 并检查 加水印 源码
参数说明:$max_file_size : 上传文件大小限制, 单位BYTE$destination_folder : 上传文件路径$watermark : 是否附加水印(1为加水印,其他为不加水印) ...
- 【HANA系列】SAP HANA快捷键大全
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[HANA系列]SAP HANA快捷键大全 ...
- C/C++ 引入头文件时 #include<***.h> 与 #include"***.h" 区别
两种情况区分: 1.#include <> 编译器只会去系统文件目录中查找,找不到就报错. 2.#include " " 编译器会先在用户目录中查找,再到编译器设定的 ...
- h2内嵌数据库使用
参考文档 1 https://www.cnblogs.com/xdp-gacl/p/4171024.html 参考文档 2 https://blog.csdn.net/mafan121/article ...
- [19/05/08-星期三] JDBC(Java DataBase Connectivity)_ORM(Object Relationship Mapping, 对象关系映射)
一.概念 基本思想: – 表结构跟类对应: 表中字段和类的属性对应:表中记录和对象对应: – 让javabean的属性名和类型尽量和数据库保持一致! – 一条记录对应一个对象.将这些查询到的对象放到容 ...
- String.indexOf()的使用方法
String.indexOf()的用途: 返回此字符串中第一个出现的指定的子字符串,如果没有找到则返回-1 源码如下: /** * Returns the index within this stri ...
- AspNetCore使用MySQL
既然NetCore开源,那么也使用开源的MySQL的数据库呢?当然NetCore不止单单配MSSQL数据库而已.今天我来讲解NetCore怎么使用MySQL进行开发. 首先新建一个NetCore项目 ...
- 实体模型集合对象转换为VO对象集合
例如: 数据库中查出来的数据为 List<RptDayMonthTarget> List<RptDayMonthTarget> list = targetService.sel ...