树链剖分剖后感
来源:互联网 发布:java 获取当天日期 编辑:程序博客网 时间:2024/06/04 00:07
1、我的脑子要被剖开啦。
2、辣鸡教材!
3、抱紧大佬博客才是最好的!
当然了,思想是在教材上看的,但是教材上一条重链一个线段树真的是……emmmm
然后就参考了正确的入门姿势,成功过了洛谷上的模板题。
树链剖分,就是将树拆成一小段一小段连续的链,得到线性的数据,但这个线性的数据使用是有限制的,比如跨段查询或修改的时候就会出错,那就只能一段一段处理,然后再合并。这时要尽量使每段更长些,那么就可以将任务尽可能多的交给线段树处理,这样复杂度是log级别的。
通过两次dfs将整颗树分成轻链和重链(同一重链在线段树上是连续的),这样将树拆成一段一段连续的链然后放到线段树上去,当查询两个节点之间的东西时,通过top[]数组和fa[]数组将这两个节点向上挪动,每次将deep[]较深的b移到fa[top[b]]处(此过程中顺便处理top[b]->b在线段树上值),重复此过程直到两个节点的top[]相同,top[]相同就意味着此时这两个节点在同一重链上,此时再查找一次(同一重链在线段树上是连续的)。
处理以节点x为根的整个子树:用线段树处理[pos[x],pos[x]+siz[x]-1]即可
查询节点x和节点y的LCA(最近公共祖先):将两点不断向上挪动并处理,直到两点的top[]相同,deep[]小的点即是最近公共祖先
处理节点x到节点y的路径上的点:将两点不断向上挪动并处理,直到两点的top[]相同,最后用线段树处理[pos[x],pos[y]]即可
处理节点x到节点y的路径上的边:将两点不断向上挪动并处理,直到两点的top[]相同,最后用线段树处理[pos[son[x]],pos[y]]即可(用每个点表示通向其父节点的边)
以洛谷模板题为例给出代码:
#include<bits/stdc++.h>using namespace std;const int MAXN=100010;struct line{ int y,next;}l[MAXN<<1];int n,m,r,p,Left,Right,x,y,z,type,cnt=0;int fa[MAXN],deep[MAXN],son[MAXN],siz[MAXN],fi[MAXN];int top[MAXN],pos[MAXN],num=0,in[MAXN],temp[MAXN];int t[MAXN<<2],delta[MAXN<<2];int read(){ int num=0;char c=getchar(); for(;c<'0'||c>'9';c=getchar()); for(;c>='0'&&c<='9';c=getchar()) num=(num<<3)+(num<<1)+c-48; return num;}void make_line(int x,int y){ l[++cnt].y=y;l[cnt].next=fi[x];fi[x]=cnt; l[++cnt].y=x;l[cnt].next=fi[y];fi[y]=cnt;}void dfs(int x,int f,int d){//第一次dfs,寻找重儿子 fa[x]=f;deep[x]=d; son[x]=0;siz[x]=1; for(int i=fi[x];i;i=l[i].next){ int v=l[i].y; if(v==f)continue; dfs(v,x,d+1); siz[x]+=siz[v]; if(siz[v]>siz[son[x]])son[x]=v; }}void dfs2(int x,int tp){//第二次dfs,构造重链,记录节点在线段树上的位置 top[x]=tp;pos[x]=++num; if(son[x])dfs2(son[x],tp); for(int i=fi[x];i;i=l[i].next){ int v=l[i].y; if(v==fa[x]||v==son[x])continue; dfs2(v,v); }}void make_tree(int l,int r,int root){//初始化 if(l==r){ t[root]=in[l]; return; } int mid=(l+r)>>1; make_tree(l,mid,root<<1); make_tree(mid+1,r,root<<1|1); t[root]=t[root<<1]+t[root<<1|1];}void push_down(int l,int r,int root){//下移delta int mid=(l+r)>>1; delta[root<<1]+=delta[root];delta[root<<1|1]+=delta[root]; delta[root<<1]%=p;delta[root<<1|1]%=p; t[root<<1]=(t[root<<1]+delta[root]*(mid-l+1))%p; t[root<<1|1]=(t[root<<1|1]+delta[root]*(r-mid))%p; delta[root]=0;}void add(int l,int r,int root){//线段树模板 if(r<Left||l>Right)return; if(Left<=l&&Right>=r){ delta[root]+=z; delta[root]%=p; t[root]=(t[root]+z*(r-l+1))%p; return; } int mid=(l+r)>>1; push_down(l,r,root); add(l,mid,root<<1); add(mid+1,r,root<<1|1); t[root]=t[root<<1]+t[root<<1|1]; return;}long long find(int l,int r,int root){//模板<<1 if(r<Left||l>Right)return 0; if(Left<=l&&Right>=r)return t[root]; int mid=(l+r)>>1; push_down(l,r,root); return (find(l,mid,root<<1)+find(mid+1,r,root<<1|1))%p;}void change(){ int a=read(),b=read();z=read(); int ta=top[a],tb=top[b]; while(ta!=tb){ if(deep[ta]>deep[tb]){ swap(ta,tb); swap(a,b); } Left=pos[tb];Right=pos[b]; add(1,n,1);//分段处理 b=fa[tb];tb=top[b]; } if(deep[a]>deep[b])swap(a,b); Left=pos[a]; Right=pos[b]; add(1,n,1);//同一重链后的处理}void query(){ int a=read(),b=read(); int ta=top[a],tb=top[b]; long long ans=0; while(ta!=tb){ if(deep[ta]>deep[tb]){ swap(ta,tb); swap(a,b); } Left=pos[tb];Right=pos[b]; ans+=find(1,n,1);//同上 ans%=p; b=fa[tb];tb=top[b]; } if(deep[a]>deep[b])swap(a,b); Left=pos[a]; Right=pos[b]; ans+=find(1,n,1); ans%=p; printf("%lld\n",ans);}void work(){ n=read();m=read();r=read();p=read(); for(int i=1;i<=n;++i) temp[i]=read(); for(int i=1;i<n;++i){ x=read();y=read(); make_line(x,y); } dfs(r,r,1); dfs2(r,r); for(int i=1;i<=n;++i) in[pos[i]]=temp[i]; make_tree(1,n,1); for(int i=1;i<=m;++i){ type=read(); if(type==1)change(); if(type==2)query(); if(type==3){ x=read(),z=read(); Left=pos[x];Right=Left+siz[x]-1;//注意子树的操作 add(1,n,1); } if(type==4){ x=read(); Left=pos[x];Right=Left+siz[x]-1; printf("%lld\n",find(1,n,1)); } }}int main(){ work(); return 0;}
模板先到这里,还有很多题要做……
- 树链剖分剖后感
- C++ 中sort 函数及 cmp 自定义规则的使用
- 【Scikit-Learn 中文文档】交叉验证
- 自定义view圆圈进度条
- 复习贴-匿名函数,内建函数
- poj2503——Babelfish
- 树链剖分剖后感
- 查询重复数据的几种方式
- Java基础之jdk1.8 JVM内存模型简述,含String常量池简单分析
- ButterKnife 专门为Android view绑定注解,专业解决各种findViewById
- 【Scikit-Learn 中文文档】优化估计器的超参数
- 数据库+3级缓存(ok请求)
- spring动态加载(刷新)配置文件
- 完全java实现一款开源的报表工具简表(JOR)
- Android使用自定义view实现标题栏、梯形布局以及二维码扫描