【笔记+模板】树链剖分
来源:互联网 发布:产品图册制作软件 编辑:程序博客网 时间:2024/05/29 09:32
- 定义
- 定义
- 解释
- 算法流程
- 重链剖分
- 维护重链
- 修改查询
- 模板
//以下定义及说明,部分来自不知名神犇的资料,侵删
定义
1.定义:
树链剖分*,计算机术语,指一种对树进行划分的算法,它先通过轻重边剖分将树分为多条链,保证每个点属于且只属于一条链,然后再通过数据结构(树状数组、SBT、SPLAY、线段树等)来维护每一条链。—-摘自百度百科
树链,就是树上的路径。剖分,就是把路径分类为重链和轻链。
每一个节点都属于且仅属于一条重链;
一般用线段树来维护,维护的是点权;
如果要维护边权,可以将边权赋给to点,根节点赋极小值。
注意求v–u路径长度时,v节点的值是fa[v]–v的边权,不应包含在内;
2.解释
变量解释:
sz[i] : 以i为根节点的子树的节点数,包括i节点本身。
son[i] : i节点的重儿子
inseg[i] : 原树内i号节点在线段树内的编号
intr[i] : 线段树内i号节点在原树内的编号
top[i] : i节点所在重链的头节点
cnt_seg : 线段树节点编号计数器
名词解释:
重儿子:siz[u]为v的子节点中siz值最大的,那么u就是v的重儿子。
轻儿子:v的其它子节点。
重边:点v与其重儿子的连边。
轻边:点v与其轻儿子的连边。
重链:由重边连成的路径。
轻链:轻边
3.性质:
性质1:如果(v,u)为轻边,则siz[u] * 2 < siz[v];(反证,若大于,则必为重边);
性质2:从根到某一点的路径上轻边、重链的个数都不大于logn。
算法流程
重链剖分:
dfs1() 找重边,求出fa,deep,son,sz
void dfs1(int x,int fx){ fa[x]=fx;deep[x]=deep[fx]+1; sz[x]=1; for(int i=first[i];i;i=next[i]){ int v=e[i].t; if(v==fx) continue; dfs1(v,x); sz[x]+=sz[v]; if(!son[x]||sz[son[x]]<sz[v])//记录重儿子 son[x]=v; } return ;}
dfs2() 将重边连成重链
以根节点为起点,往下拉重链,不在当前重链上的节点,以其为根节点往下拉重链
1.对于重儿子,为了使一条重链各边在线段树中连续分布,应当进行dfs_2(son[v]);
2.对于轻儿子,以其本身重新拉重链。
void dfs2(int u,int tp){ top[u]=tp; inseg[u]=++cnt_seg; intr[cnt_seg]=u; if(!son[u]) return ;//若为叶子节点,直接return dfs2(son[u],tp);//重儿子 for(int i=first[u];i;i=next[i]){ int v=e[i].t; if(v==fa[u]||v==son[u]) continue; dfs2(v,v);//轻儿子 } return ;}
维护重链:
一条重链就是一段连续区间,将所有重链首尾相接,用线段树维护。
修改/查询:
修改查询操作类似,以修改为例。
单点修改
直接用线段树修改单点。
区间修改 (u,v)
1.u,v在一条重链上,直接用线段树修改区间(inseg[u],inseg[v])。
2.不在一条重链上,边修改边向一条重链上靠。
选择deep[top[]]大的点u,//防止跳过界
线段树修改区间(inseg[top[u]],inseg[u]);
将u跳至fa[top[u]],直至u,v在一条重链上,重复1。
注:
跳至同一条重链时,深度小的点即为lca,这就是树剖求lca,详见小机房的树。
蒋一瑶神犇的课件
https://wenku.baidu.com/view/a088de01eff9aef8941e06c3.html
模板:
题目:树的统计
http://codevs.cn/problem/2460/
#include<iostream>#include<cstdio>using namespace std;const int N=50000+60,M=75000+60;int n,m,a,b,c,tot,cnt_seg;struct seg_tree{ int l,r,sum,maxx;}seg[N<<2];int e[M],first[N],next[M],fa[N],deep[N],top[N],inseg[N],intr[N],sz[N],son[N],val[N];void build(int ff,int tt){ e[++tot]=tt; next[tot]=first[ff]; first[ff]=tot; return ;}void dfs1(int x,int fx){ fa[x]=fx;deep[x]=deep[fx]+1; sz[x]=1; for(int i=first[x];i;i=next[i]){ if(e[i]==fx) continue; dfs1(e[i],x); sz[x]+=sz[e[i]]; if(!son[x]||sz[son[x]]<sz[e[i]]) son[x]=e[i]; } return ;}void dfs2(int t,int tp){ top[t]=tp; inseg[t]=++cnt_seg; intr[cnt_seg]=t; if(!son[t]) return ; dfs2(son[t],tp); for(int i=first[t];i;i=next[i]){ int v=e[i]; if(v==fa[t]||v==son[t]) continue; dfs2(v,v); } return ;}void updata(int num){ seg[num].maxx=max(seg[num<<1].maxx,seg[num<<1|1].maxx); seg[num].sum=seg[num<<1].sum+seg[num<<1|1].sum; return ;}void make_seg(int num,int ll,int rr){ seg[num].l=ll;seg[num].r=rr; if(ll==rr){ seg[num].maxx=seg[num].sum=val[intr[ll]]; //易错。intr而不是inseg,线段树中ll点值是ll点在原树内的标号的值 return ; } int mid=ll+rr>>1; make_seg(num<<1,ll,mid); make_seg(num<<1|1,mid+1,rr); updata(num); return ;}int asksum_seg(int num,int ll,int rr){ if(ll>rr) swap(ll,rr); if(seg[num].l>=ll&&seg[num].r<=rr) return seg[num].sum; int mid=seg[num].l+seg[num].r>>1; int ret=0; if(ll<=mid) ret+=asksum_seg(num<<1,ll,rr); if(rr>mid) ret+= asksum_seg(num<<1|1,ll,rr); return ret;}int askmax_seg(int num,int ll,int rr){ if(ll>rr) swap(ll,rr); if(seg[num].l>=ll&&seg[num].r<=rr) return seg[num].maxx; int mid=seg[num].l+seg[num].r>>1; int ret=-2e9; if(ll<=mid) ret=max(ret,askmax_seg(num<<1,ll,rr)); if(rr>mid) ret=max(ret,askmax_seg(num<<1|1,ll,rr)); return ret;}int asksum(int x,int y){ int ans=0; while(top[x]!=top[y]){ if(deep[top[y]]>deep[top[x]])//比较top的深度,而非xy的深度。 swap(x,y); ans+=asksum_seg(1,inseg[top[x]],inseg[x]); x=fa[top[x]]; } ans+=asksum_seg(1,inseg[x],inseg[y]); return ans;}int askmax(int x,int y){ int ans=-2e9; while(top[x]!=top[y]){ if(deep[top[y]]>deep[top[x]]) swap(x,y); ans=max(ans,askmax_seg(1,inseg[top[x]],inseg[x])); x=fa[top[x]]; } ans=max(ans,askmax_seg(1,inseg[x],inseg[y])); return ans;}void change(int num,int x,int v){ if(seg[num].l==seg[num].r){ seg[num].sum=v; seg[num].maxx=v; return ; } int mid=seg[num].l+seg[num].r>>1; if(x<=mid) change(num<<1,x,v); else change(num<<1|1,x,v); updata(num); return ;}int main(){ scanf("%d",&n); for(int i=1;i<n;i++){ scanf("%d%d",&a,&b); build(a,b);build(b,a); } for(int i=1;i<=n;i++) scanf("%d",&val[i]); dfs1(1,0); dfs2(1,1); make_seg(1,1,cnt_seg); scanf("%d",&m); for(int i=1;i<=m;i++){ string s; cin>>s; scanf("%d%d",&a,&b); if(s=="CHANGE") change(1,inseg[a],b); if(s=="QMAX") printf("%d\n",askmax(a,b)); //易错,这里要写a,b,而非inseg[a],inseg[b],因为重链在原树内,而不是在线段树内,线段树内只有多段连续的区间。 if(s=="QSUM") printf("%d\n",asksum(a,b));//易错,同上 } return 0;}
- 【笔记+模板】树链剖分
- Xdoclet学习笔记(模板)
- xsl学习笔记-模板
- 模板学习笔记(一)
- 模板学习笔记
- 模板笔记(1)
- mako模板学习笔记
- D3D学习笔记:模板
- C++模板笔记
- mako模板学习笔记
- C++模板笔记
- mako模板笔记
- Smarty模板学习笔记
- luci笔记4,模板
- 重写Car模板笔记
- C++笔记 简单模板
- c++模板 学习笔记
- C++模板机制笔记
- 第四周 【项目2
- opencv彩色图像通道分离
- 用python搭建一个最简单的Web服务器
- 12.construct command
- HNU OJ题库1005F字符串匹配问题
- 【笔记+模板】树链剖分
- Mysql 图表知识点收录
- 协程的概念及Python中利用第三方库gevent使用协程
- MacBookPro2017下卸载Java9
- python爬虫随笔3 妹子图爬虫
- bzoj 2662: [BeiJing wc2012]冻结
- 第四周项目2——建设“单链表”算法库
- [BZOJ 4569][SCOI 2016] 萌萌哒 区间并查集(ST表思想)
- 包装类