关于数链剖分我在网上看到的有几个比较好的讲解,本篇主要是对AC代码的注释(感谢各位witer的提供)

这是讲解

http://www.cnblogs.com/kuangbin/archive/2013/08/15/3259083.html

另一个是百度文库

http://wenku.baidu.com/link?url=DY8CAbwdjitIiv8XQsHmVPi--dQAqw5z6dc_6N1Plh4u5Nfc1aCADQm4oAvt4Sqe1mXSixezzK4lRxofQKMX9cNzJwYwQhpGJZuMSTI3Ktq

这是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++、算法、树结构)的更多相关文章

  1. 浅谈树链剖分 F&Q

    这是一篇迟来的博客,由于我懒得写文章,本篇以两个问题阐述笔者对树链剖分的初步理解. Q1:树链剖分解决什么问题? 树链剖分,就是把一棵树剖分成若干连续的链,将这些链里的数据映射在线性数组上维护.比方说 ...

  2. 蒟蒻浅谈树链剖分之一——两个dfs操作

    树链剖分,顾名思义就是将树形的结构剖分成链,我们以此便于在链上操作 首先我们需要明白在树链剖分中的一些概念 重儿子:某节点所有儿子中子树最多的儿子 重链:有重儿子构成的链 dfs序:按重儿子优先遍历时 ...

  3. 【算法学习】【洛谷】树链剖分 & P3384 【模板】树链剖分 P2146 软件包管理器

    刚学的好玩算法,AC2题,非常开心. 其实很早就有教过,以前以为很难就没有学,现在发现其实很简单也很有用. 更重要的是我很好调试,两题都是几乎一遍过的. 介绍树链剖分前,先确保已经学会以下基本技巧: ...

  4. 算法笔记--树的直径 && 树形dp && 虚树 && 树分治 && 树上差分 && 树链剖分

    树的直径: 利用了树的直径的一个性质:距某个点最远的叶子节点一定是树的某一条直径的端点. 先从任意一顶点a出发,bfs找到离它最远的一个叶子顶点b,然后再从b出发bfs找到离b最远的顶点c,那么b和c ...

  5. 算法复习——树链剖分模板(bzoj1036)

    题目: 题目背景 ZJOI2008 DAY1 T4 题目描述 一棵树上有 n 个节点,编号分别为 1 到 n ,每个节点都有一个权值 w .我们将以下面的形式来要求你对这棵树完成一些操作:I.CHAN ...

  6. 树链剖分 (求LCA,第K祖先,轻重链剖分、长链剖分)

      2020/4/30   15:55 树链剖分是一种十分实用的树的方法,用来处理LCA等祖先问题,以及对一棵树上的节点进行批量修改.权值和查询等有奇效. So, what is 树链剖分? 可以简单 ...

  7. 培训补坑(day8:树上倍增+树链剖分)

    补坑补坑.. 其实挺不理解孙爷为什么把这两个东西放在一起讲..当时我学这一块数据结构都学了一周左右吧(超虚的) 也许孙爷以为我们是省队集训班... 好吧,虽然如此,我还是会认真写博客(保证初学者不会出 ...

  8. BZOJ 1036: [ZJOI2008]树的统计Count [树链剖分]【学习笔记】

    1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 14302  Solved: 5779[Submit ...

  9. 树链剖分求LCA

    树链剖分中各种数组的作用: siz[]数组,用来保存以x为根的子树节点个数 top[]数组,用来保存当前节点的所在链的顶端节点 son[]数组,用来保存重儿子 dep[]数组,用来保存当前节点的深度 ...

随机推荐

  1. iOS7 UIKit动力学-碰撞特性UICollisionBehavior 下

    上文讲到了为window加一个边界.实现碰撞的效果,接下来我们将提到一个托付方法: - (void)collisionBehavior:(UICollisionBehavior *)behavior ...

  2. 让你提前认识软件开发(23):怎样在C语言中运行shell命令?

    第1部分 又一次认识C语言 怎样在C语言中运行shell命令? [文章摘要] Linux操作系统具备开源等诸多优秀特性,因此在很多通信类软件(主流开发语言为C语言)中,开发平台都迁移到了Linux上, ...

  3. 数据分析与R语言

    数据结构 创建向量和矩阵 函数c(), length(), mode(), rbind(), cbind() 求平均值,和,连乘,最值,方差,标准差 函数mean(), sum(), min(), m ...

  4. 【迪杰斯特拉双关键字最短路】【HDU3790】【最短路径问题】

    题目大意: 给你n个点,m条无向边,每条边都有长度d和花费p,给你起点s终点t,要求输出起点到终点的最短距离及其花费,如果最短距离有多条路线,则输出花费最少的. 只需要再更新的时候判断一下就好 voi ...

  5. UMeditor 百度编辑器Mini学习

    准备开始研究百度的mini编辑器 1.4.3的昨天配置好了 ,也可以用了(.net 3.5) 为此我废了好大的力 研究了一天才弄好   结果今天一来上司就说 这个1.4.3 我们没必要用这么大的  用 ...

  6. 如何将java web项目上线/部署到公网

    关于如何将java web上线,部署到公网,让全世界的人都可以访问的问题. 1.在myeclipse中开发好项目,打包成war格式,不会的同学参考以下 http://zhidao.baidu.com/ ...

  7. Hadoop学习资料收集

    1.漫画HDFS工作原理  http://blog.csdn.net/netcoder/article/details/7442779 2.马哥教育 http://mageedu.blog.51cto ...

  8. Android.Hacks.01_Centering views using weights

    Android.Hacks读书笔记01 #1#权重布局之解析: LinearLayout ’s android:weightSum      LinearLayout ’s child android ...

  9. BroadcastReceiver浅析

    1.什么是BroadcastReceiver? 本质上是属于一个监听器,但onXxxListenter只是程序级别的监听器,当程序退出时候监听器也随之关闭.而BroadcastReceiver是系统级 ...

  10. nginx,wsgi,flask之间的关系

    之前看写flask 应用的一些疑问,百度上的答案解释的不错,这里记着以后可以看看Web 服务器层对于传统的客户端 - 服务器架构,客户端向服务器发送请求,服务器接收请求,处理请求,最后给客户端返回请求 ...