博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
浅谈树链剖分(C++、算法、树结构)
阅读量:5241 次
发布时间:2019-06-14

本文共 3763 字,大约阅读时间需要 12 分钟。

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

这是讲解

另一个是百度文库

这是AC代码(以bzoj1036为例题)

但是我相信很多人有和我一样的经历,就是看各种大神的代码的时候只是膜拜他们的长度和复杂,但是大神们却并不对代码进行讲解,导致我们和多人看不懂。下来还得自己理解。

这样会花费很长的时间。。。。TAT!

下面是我对AC代码的注释,原文并没有那么多。其中没有关于线段树的知识,只是对剖分时进行了解释。对于不了解线段树的读者请自行百度。。。。。

希望能对读者有较大的帮助。

#include
#include
#include
#include
#include
#include
using namespace std;const int MAXN = 30010;struct Edge//E[i].to为边E[i]指向的点.{ // E[i].next为边E的上一条边(即E与E的上一条边由同一节点发出). int to,next;}edge[MAXN*2];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=0;pos=0; memset(head,0,sizeof(head)); memset(son,-1,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]=1;//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+1);//v 深搜到的点 u v的父亲(前驱) 深度+1 num[u]+=num[v];//父亲包含的点数=儿子包含的点数+1 if(son[u]==-1||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]==-1) 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*3];void push_up(int i){ segTree[i].sum=segTree[i<<1].sum+segTree[(i<<1)|1].sum; segTree[i].Max=max(segTree[i<<1].Max,segTree[(i<<1)|1].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)/2; build(i<<1,l,mid); build((i<<1)|1,mid+1,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)/2; if(k<=mid)update(i<<1,k,val); else update((i<<1)|1,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)/2; if(r<=mid) return queryMax(i<<1,l,r); else if(l > mid)return queryMax((i<<1)|1,l,r); else return max(queryMax(i<<1,l,mid),queryMax((i<<1)|1,mid+1,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)/2; if(r<=mid)return querySum(i<<1,l,r); else if(l>mid)return querySum((i<<1)|1,l,r); else return querySum(i<<1,l,mid)+querySum((i<<1)|1,mid+1,r);}int findMax(int u,int v)//查询u->v路径上节点的最大权值{ int f1=top[u],f2=top[v]; int tmp=-1000000000; while(f1!=f2){ if(deep[f1]
deep[v]) swap(u,v); return max(tmp,queryMax(1,p[u],p[v]));}int findSum(int u,int v) //查询u->v路径上节点的权值的和{ int f1=top[u],f2=top[v];//记录所属链的根节点 int tmp=0; while(f1!=f2){ if(deep[f1]
deep[v]) swap(u,v); return tmp+querySum(1,p[u],p[v]);}int main() {// freopen("in.in","r",stdin);// freopen("out.out","w",stdout); int n; int q; char op[20]; int u,v; while(scanf("%d",&n)==1){ init(); for(int i=1;i
v路径上点权的最大值 else printf("%d\n",findSum(u,v));//查询路径上点权的和 } } return 0;}

程序员不是打字员而是作家!

转载于:https://www.cnblogs.com/xtx1999/p/4616345.html

你可能感兴趣的文章
Item 9: Avoid Conversion Operators in Your APIs(Effective C#)
查看>>
js去除空格
查看>>
学习Spring Boot:(二十八)Spring Security 权限认证
查看>>
IT学习神器——慕课网App获App Store、Android应用市场重磅推荐
查看>>
Linux网络状态工具ss命令使用详解
查看>>
深入浅出JavaScript(2)—ECMAScript
查看>>
编程珠玑第十一章----排序
查看>>
Face The Right Way POJ - 3276 (开关问题)
查看>>
STEP2——《数据分析:企业的贤内助》重点摘要笔记(六)——数据描述
查看>>
变量的命名规范
查看>>
手机端自动跳转
查看>>
react中进入某个详情页URL路劲参数Id获取问题
查看>>
首届.NET Core开源峰会
查看>>
ViewPager的onPageChangeListener里面的一些方法参数:
查看>>
python pdf转word
查看>>
文本相似度比较(网页版)
查看>>
Jenkins关闭、重启,Jenkins服务的启动、停止方法。
查看>>
CF E2 - Array and Segments (Hard version) (线段树)
查看>>
Linux SPI总线和设备驱动架构之四:SPI数据传输的队列化
查看>>
SIGPIPE并产生一个信号处理
查看>>