关于数链剖分我在网上看到的有几个比较好的讲解,本篇主要是对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. 蒟蒻浅谈树链剖分之一——两个dfs操作

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

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

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

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

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

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

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

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

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

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

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

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

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

  8. 树链剖分求LCA

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

  9. 树链剖分I 原理

    树链剖分(Heavy Light Decomposition, HLD)是一种将对[树上两点间的路径]上[边或点]的[修改与查询]转化到[序列]上来处理的方法. 目的:将树的边或点转化到一个线性结构( ...

随机推荐

  1. PHP访问带密码的Redis

    1. 设置Redis密码,以提供远程登陆打开redis.conf配置文件,找到requirepass,然后修改如下: requirepass yourpassword yourpassword就是re ...

  2. 《Memcache学习总结》[PDF]发布

    <Memcache学习总结>[PDF]发布 百度网盘共享: http://pan.baidu.com/s/1mgvayQO  版本号: V1.2 最后跟新: 2015-04-01 讨论组: ...

  3. Surround the Trees---hdu1392(凸包GraHam模板)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1392 题意:有n棵树,每棵树有一个坐标,想用一些绳子把这些树包含起来,求需要绳子的长度: 就是求凸包的 ...

  4. 【转】[WCF REST] 帮助页面与自动消息格式(JSON/XML)选择

    可以说WebHttpBinding和WebHttpBehavior是整个Web HTTP编程模型最为核心的两个类型,前者主要解决消息编码问题,而余下的工作基本上落在了终结点行为WebHttpBehav ...

  5. 《think in python》学习-7

    think in python 7 迭代 对一个变量可以进行多次赋值 是合法的. 例如以下: bruce = 5 print bruce bruce = 7 print bruce 因为有了多重赋值 ...

  6. You can Solve a Geometry Problem too(判断两线段是否相交)

    You can Solve a Geometry Problem too Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/3 ...

  7. word发布博客

    无向图双连通部件(双连通分量) 关节点和桥边的定义: 双连通部件的性质   每一个双连通部件应该包含至少两个顶点,除非整个无向图只包含一个顶点   如果两个双连通部件包含同一个顶点,那么这个共有的顶点 ...

  8. Centos7安装python3并与python2共存

    1.查看是否已经安装Python CentOS 7.2 默认安装了python2.7.5 因为一些命令要用它比如yum 它使用的是python2.7.5. 使用 python -V 命令查看一下是否安 ...

  9. oracle12c创建用户指定表空间

    --1.创建临时空间 create temporary tablespace zyj_temp tempfile 'D:\app2\user\virtual\oradata\orcl\zyj_temp ...

  10. 洛谷 P1878 舞蹈课 解题报告

    P1878 舞蹈课 题目描述 有\(n\)个人参加一个舞蹈课.每个人的舞蹈技术由整数来决定.在舞蹈课的开始,他们从左到右站成一排.当这一排中至少有一对相邻的异性时,舞蹈技术相差最小的那一对会出列并开始 ...