浅谈树链剖分(C++、算法、树结构)
关于数链剖分我在网上看到的有几个比较好的讲解,本篇主要是对AC代码的注释(感谢各位witer的提供)
这是讲解
http://www.cnblogs.com/kuangbin/archive/2013/08/15/3259083.html
另一个是百度文库
这是AC代码(以bzoj1036为例题)
http://www.cnblogs.com/kuangbin/archive/2013/08/15/3259083.html
但是我相信很多人有和我一样的经历,就是看各种大神的代码的时候只是膜拜他们的长度和复杂,但是大神们却并不对代码进行讲解,导致我们和多人看不懂。下来还得自己理解。
这样会花费很长的时间。。。。TAT!
下面是我对AC代码的注释,原文并没有那么多。其中没有关于线段树的知识,只是对剖分时进行了解释。对于不了解线段树的读者请自行百度。。。。。
希望能对读者有较大的帮助。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<string>
using namespace std;
const int MAXN = ;
struct Edge//E[i].to为边E[i]指向的点.
{// E[i].next为边E的上一条边(即E与E的上一条边由同一节点发出).
int to,next;
}edge[MAXN*];
int head[MAXN],tot;//head[j]为节点j的最后一条发出边.
int top[MAXN]; //top[v] 表示v所在的重链的顶端节点
int fa[MAXN]; //父亲节点
int deep[MAXN];//深树的度
int num[MAXN]; //num[v]表示以v为根的子树的节点数
int p[MAXN]; //p[v]表示v在线段树中的位置
int fp[MAXN];//和p数组相反
int son[MAXN];//重儿子
int pos;
void init()
{
tot=;pos=;
memset(head,,sizeof(head));
memset(son,-,sizeof(son));
}
void addedge(int u,int v)
{
edge[++tot].to=v;edge[tot].next=head[u];head[u]=tot;
}
void dfs1(int u,int pre,int d) //第一遍dfs求出fa,deep,num,son
{
deep[u]=d;//u 深搜到的点 pre u的前驱 d 深度
fa[u]=pre;//完成父亲数组
num[u]=;//u 包含的点数
for(int i=head[u];i;i=edge[i].next){
int v=edge[i].to;//边i的指向点
if(v!=pre){//判断是否是i的父亲,若是跳出不是继续深搜
dfs1(v,u,d+);//v 深搜到的点 u v的父亲(前驱) 深度+1
num[u]+=num[v];//父亲包含的点数=儿子包含的点数+1
if(son[u]==-||num[v]>num[son[u]])//son==-1时,即没有标记重儿子,直接标记
son[u]=v;// num[v]>num[son[u]]如果v包含的节点数大于其父亲的重儿子的节点数,v变为
}// 其父亲的重儿子
}
}
void getpos(int u,int sp)//将重儿子连成重链
{
top[u]=sp;//sp为传递变量,即若sp不变,DFS过程中的重儿子均属于以sp为根的重链
p[u]=pos++;//将重链的节点放在数组p中,来构建线段树
fp[p[u]]=u;//反之
if(son[u]==-) return;//若son为-1,则该点为叶子节点,返回
getpos(son[u],sp);//DFS对u的重儿子,将其加入重链中,sp不变
for(int i=head[u];i;i=edge[i].next){//边的循环(将一条重链添加完后再对轻边进行添加)
int v=edge[i].to;//v是边i的指向点
if(v!=son[u]&&v!=fa[u]) getpos(v,v);//(v!=fa[u])处理v是u的父亲的情况
}//若v是u的儿子但不是重儿子(即其属于轻边),以自己为根(修改sp)继续DFS形成新的重链
}
struct Node//线段树的点
{
int l,r;
int sum;
int Max;
}segTree[MAXN*];
void push_up(int i)
{
segTree[i].sum=segTree[i<<].sum+segTree[(i<<)|].sum;
segTree[i].Max=max(segTree[i<<].Max,segTree[(i<<)|].Max);
}
int s[MAXN];
void build(int i,int l,int r)//建线段树(不懂的看线段树的讲解)
{
segTree[i].l=l;
segTree[i].r=r;
if(l==r){
segTree[i].sum=segTree[i].Max=s[fp[l]];
return;
}
int mid=(l+r)/;
build(i<<,l,mid);
build((i<<)|,mid+,r);
push_up(i);//用来储存树上的和、最大值
}
void update(int i,int k,int val)//更新线段树的第k个值为val
{
if(segTree[i].l==k&&segTree[i].r==k){
segTree[i].sum=segTree[i].Max=val;
return;
}
int mid=(segTree[i].l+segTree[i].r)/;
if(k<=mid)update(i<<,k,val);
else update((i<<)|,k,val);
push_up(i);
}
int queryMax(int i,int l,int r)//查询线段树[l,r]区间的最大值
{
if(segTree[i].l==l&&segTree[i].r==r){
return segTree[i].Max;
}
int mid=(segTree[i].l+segTree[i].r)/;
if(r<=mid) return queryMax(i<<,l,r);
else if(l > mid)return queryMax((i<<)|,l,r);
else return max(queryMax(i<<,l,mid),queryMax((i<<)|,mid+,r));
}
int querySum(int i,int l,int r) //查询线段树[l,r]区间的和
{
if(segTree[i].l==l&&segTree[i].r==r)
return segTree[i].sum;
int mid=(segTree[i].l+segTree[i].r)/;
if(r<=mid)return querySum(i<<,l,r);
else if(l>mid)return querySum((i<<)|,l,r);
else return querySum(i<<,l,mid)+querySum((i<<)|,mid+,r);
}
int findMax(int u,int v)//查询u->v路径上节点的最大权值
{
int f1=top[u],f2=top[v];
int tmp=-;
while(f1!=f2){
if(deep[f1]<deep[f2]){
swap(f1,f2);
swap(u,v);
}
tmp=max(tmp,queryMax(,p[f1],p[u]));
u=fa[f1];
f1=top[u];
}
if(deep[u]>deep[v]) swap(u,v);
return max(tmp,queryMax(,p[u],p[v]));
}
int findSum(int u,int v) //查询u->v路径上节点的权值的和
{
int f1=top[u],f2=top[v];//记录所属链的根节点
int tmp=;
while(f1!=f2){
if(deep[f1]<deep[f2]){//同时向上跳
swap(f1,f2);
swap(u,v);
}
tmp+=querySum(,p[f1],p[u]);
u=fa[f1];
f1=top[u];
}
if(deep[u]>deep[v]) swap(u,v);
return tmp+querySum(,p[u],p[v]);
}
int main()
{
// freopen("in.in","r",stdin);
// freopen("out.out","w",stdout);
int n;
int q;
char op[];
int u,v;
while(scanf("%d",&n)==){
init();
for(int i=;i<n;i++){
scanf("%d%d",&u,&v);
addedge(u,v);
addedge(v,u);
}
for(int i=;i<=n;i++)
scanf("%d",&s[i]);
dfs1(,,);
getpos(,);
build(,,pos-);
scanf("%d",&q);
while(q--){
scanf("%s%d%d",op,&u,&v);
if(op[]=='C') update(,p[u],v);//修改单点的值
else if(strcmp(op,"QMAX") == )
printf("%d\n",findMax(u,v));//查询u->v路径上点权的最大值
else printf("%d\n",findSum(u,v));//查询路径上点权的和
}
}
return ;
}
程序员不是打字员而是作家!
浅谈树链剖分(C++、算法、树结构)的更多相关文章
- 浅谈树链剖分 F&Q
这是一篇迟来的博客,由于我懒得写文章,本篇以两个问题阐述笔者对树链剖分的初步理解. Q1:树链剖分解决什么问题? 树链剖分,就是把一棵树剖分成若干连续的链,将这些链里的数据映射在线性数组上维护.比方说 ...
- 蒟蒻浅谈树链剖分之一——两个dfs操作
树链剖分,顾名思义就是将树形的结构剖分成链,我们以此便于在链上操作 首先我们需要明白在树链剖分中的一些概念 重儿子:某节点所有儿子中子树最多的儿子 重链:有重儿子构成的链 dfs序:按重儿子优先遍历时 ...
- 【算法学习】【洛谷】树链剖分 & P3384 【模板】树链剖分 P2146 软件包管理器
刚学的好玩算法,AC2题,非常开心. 其实很早就有教过,以前以为很难就没有学,现在发现其实很简单也很有用. 更重要的是我很好调试,两题都是几乎一遍过的. 介绍树链剖分前,先确保已经学会以下基本技巧: ...
- 算法笔记--树的直径 && 树形dp && 虚树 && 树分治 && 树上差分 && 树链剖分
树的直径: 利用了树的直径的一个性质:距某个点最远的叶子节点一定是树的某一条直径的端点. 先从任意一顶点a出发,bfs找到离它最远的一个叶子顶点b,然后再从b出发bfs找到离b最远的顶点c,那么b和c ...
- 算法复习——树链剖分模板(bzoj1036)
题目: 题目背景 ZJOI2008 DAY1 T4 题目描述 一棵树上有 n 个节点,编号分别为 1 到 n ,每个节点都有一个权值 w .我们将以下面的形式来要求你对这棵树完成一些操作:I.CHAN ...
- 树链剖分 (求LCA,第K祖先,轻重链剖分、长链剖分)
2020/4/30 15:55 树链剖分是一种十分实用的树的方法,用来处理LCA等祖先问题,以及对一棵树上的节点进行批量修改.权值和查询等有奇效. So, what is 树链剖分? 可以简单 ...
- 培训补坑(day8:树上倍增+树链剖分)
补坑补坑.. 其实挺不理解孙爷为什么把这两个东西放在一起讲..当时我学这一块数据结构都学了一周左右吧(超虚的) 也许孙爷以为我们是省队集训班... 好吧,虽然如此,我还是会认真写博客(保证初学者不会出 ...
- BZOJ 1036: [ZJOI2008]树的统计Count [树链剖分]【学习笔记】
1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 14302 Solved: 5779[Submit ...
- 树链剖分求LCA
树链剖分中各种数组的作用: siz[]数组,用来保存以x为根的子树节点个数 top[]数组,用来保存当前节点的所在链的顶端节点 son[]数组,用来保存重儿子 dep[]数组,用来保存当前节点的深度 ...
随机推荐
- SQL Server中的sysobjects
摘自:http://www.cnblogs.com/bugY/archive/2011/09/21/2184182.html 关于SQL Server数据库的一切信息都保存在它的系统表格里.我怀疑你是 ...
- 加濾鏡效果GlowTween
/** * * new GlowTween(xxxx, 0xFFFF00); * new GlowTween(xxxx, 0x00FFFF); * GlowTween */ package com.r ...
- 图片延迟加载并等比缩放,一个简单的JQuery插件
使用方法: $(".viewArea img").zoom({height:74,width:103}); (function($){ $.fn.zoom = function(s ...
- 有用的HTML+CSS片段
HTML5页面模板 现在国外很多制作新网站直接使用了HTML5代码,当然我们也得跟上,下面是一个常用的HTML5默认模板,就像你用Dreamweaver新建一个HTML文件时的代码,只不过现在这个是H ...
- Mysql Binlog Dump原理
Register 首先,我们需要伪造一个slave,向master注册,这样master才会发送binlog event.注册很简单,就是向master发送COM_REGISTER_SLAVE命令,带 ...
- XSD (xml Schema Definition)
.xsd文件是定义DataSet的XML文件,利用XML文件的结构优势容易可视化地设计DataSet,设计完它会生成相应的.cs文件,里面的内容就是对应的类型化的DataSet.你的代码里的DataA ...
- 【27前端】背景半透明rgba LESS实践
今天有看到司徒正美<背景半透明rgba最佳实践>的文章和里面推荐的一个在线工具CSS背景颜色属性值转换 . 于是联系到自己的less库,新技能Get. 内容如下: /*在你的less库中 ...
- dom处理配置文件_待完善
import java.io.File;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java ...
- 使用Open Live Writer 的代码高亮插件体验
由于windows live writer 2012 已经停止服务,转而推出开源项目Open Live Writer .虽然Open Live Writer 也没怎么更新,官网更是一个插件都没有放出来 ...
- C语言静态函数静态变量
C语言程序可以看成由一系列外部对象构成,这些外部对象可能是变量或函数.而内部对象是指定义在函数内部的函数参数及变量. 外部变量定义在函数之外,因此可以在许多函数中使用.由于C语言不允许在一个函数中定义 ...