树链剖分

树链剖分是一种对树的分治, 可以把树上的任意一条链分解为 \(O(\log n)\) 条在dfs序上相邻的子链, 便于数据结构(如线段树)来维护.

另外, 子树在dfs序上也是一个连续的区间, 同样可以利用数据结构维护.

重链剖分保证了一些性质:

  1. 每个点到根的重链条数为 \(O(\log n)\).

Code

//sgt:
// chg(v,l,r,rt,rl,rr) 区间修改
// que(l,r,rt,rl,rr) 区间查询
//树剖
int sz[nsz],fa[nsz],son[nsz],dep[nsz];//got through dfs1
int dfn[nsz],pd=0,idfn[nsz],top[nsz];//got through dfs2
void dfs1(int p){
sz[p]=1;
forg(p,i,v){
if(v==fa[p])continue;
fa[v]=p,dep[v]=dep[p]+1;
dfs1(v);
sz[p]+=sz[v];
if(son[p]==0||sz[v]>sz[son[p]])son[p]=v;
}
}
void dfs2(int p){
dfn[++pd]=p,idfn[p]=pd;
top[p]=(p==son[fa[p]]?top[fa[p]]:p);
if(son[p])dfs2(son[p]);
forg(p,i,v){
if(v==fa[p]||v==son[p])continue;
dfs2(v);
}
} //query template
void treec(int x,int y,int v){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
//do something
chg(v,idfn[top[x]],idfn[x],1,1,n);
//end
x=fa[top[x]];
}
if(dep[x]<dep[y])swap(x,y);
//also do sth
chg(v,idfn[y],idfn[x],1,1,n);
//end
}

例题: luogu3384-【模板】树链剖分

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<set>
#include<map>
using namespace std;
#define rep(i,l,r) for(register int i=(l);i<=(r);++i)
#define repdo(i,l,r) for(register int i=(l);i>=(r);--i)
#define il inline
typedef double db;
typedef long long ll; //---------------------------------------
const int nsz=1e5+50;
int n,m,r,nmod,line[nsz]; //g
struct te{int t,pr;}edge[nsz*2];
int hd[nsz],pe=1;
void adde(int f,int t){edge[++pe]=(te){t,hd[f]};hd[f]=pe;}
void adddb(int f,int t){adde(f,t);adde(t,f);}
#define forg(p,i,v) for(int i=hd[p],v=edge[i].t;i;i=edge[i].pr,v=edge[i].t) //sgt
void addv(int &a,int b){a=(a+b)%nmod;}
struct tnd{int sum,tag;}tree[nsz*4];
#define ls(p) ((p)<<1)
#define rs(p) (((p)<<1)|1)
void addp(int p,int v,int l){addv(tree[p].sum,(ll)v*l%nmod);addv(tree[p].tag,v);}
void pushd(int p,int l1,int l2){
int tmp=tree[p].tag;
addp(ls(p),tmp,l1);
addp(rs(p),tmp,l2);
tree[p].tag=0;
} void chg(int v,int l,int r,int rt,int rl,int rr){
if(l<=rl&&rr<=r){addp(rt,v,rr-rl+1);return;}
int mid=(rl+rr)>>1;
if(l<=mid)chg(v,l,r,ls(rt),rl,mid);
if(r>mid)chg(v,l,r,rs(rt),mid+1,rr);
addv(tree[rt].sum,(ll)v*(min(r,rr)-max(l,rl)+1)%nmod);
}
int que(int l,int r,int rt,int rl,int rr){
if(l<=rl&&rr<=r){return tree[rt].sum;}
int mid=(rl+rr)>>1,res=0;
if(tree[rt].tag)pushd(rt,mid-rl+1,rr-mid);
if(l<=mid)res=que(l,r,ls(rt),rl,mid);
if(r>mid)addv(res,que(l,r,rs(rt),mid+1,rr));
return res;
}
void pr(int rt,int rl,int rr){
printf("rt=%d rl=%d rr=%d sum=%d tag=%d\n",rt,rl,rr,tree[rt].sum,tree[rt].tag);
if(rl==rr)return;
int mid=(rl+rr)>>1;
pushd(rt,mid-rl+1,rr-mid);
pr(ls(rt),rl,mid);
pr(rs(rt),mid+1,rr);
} //树剖
int sz[nsz],fa[nsz],son[nsz],dep[nsz];//got through dfs1
int dfn[nsz],pd=0,idfn[nsz],top[nsz];//got through dfs2
void dfs1(int p){
sz[p]=1;
forg(p,i,v){
if(v==fa[p])continue;
fa[v]=p,dep[v]=dep[p]+1;
dfs1(v);
sz[p]+=sz[v];
if(son[p]==0||sz[v]>sz[son[p]])son[p]=v;
}
}
void dfs2(int p){
dfn[++pd]=p,idfn[p]=pd;
top[p]=(p==son[fa[p]]?top[fa[p]]:p);
if(son[p])dfs2(son[p]);
forg(p,i,v){
if(v==fa[p]||v==son[p])continue;
dfs2(v);
}
} int treeq(int x,int y){
int res=0;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
addv(res,que(idfn[top[x]],idfn[x],1,1,n));
x=fa[top[x]];
}
if(dep[x]<dep[y])swap(x,y);
addv(res,que(idfn[y],idfn[x],1,1,n));
return res;
} void treec(int x,int y,int v){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
chg(v,idfn[top[x]],idfn[x],1,1,n);
x=fa[top[x]];
}
if(dep[x]<dep[y])swap(x,y);
chg(v,idfn[y],idfn[x],1,1,n);
} void init(){
dfs1(r),dfs2(r);
rep(i,1,n)chg(line[i],idfn[i],idfn[i],1,1,n);
} int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>m>>r>>nmod;
rep(i,1,n)cin>>line[i];
int a,b,c,d;
rep(i,1,n-1)cin>>a>>b,adddb(a,b);
init();
// pr(1,1,n);
rep(i,1,m){
cin>>a>>b;
switch(a){
case 1:
cin>>c>>d;
treec(b,c,d%nmod);
break;
case 2:
cin>>c;
cout<<treeq(b,c)<<'\n';
break;
case 3:
cin>>c;
chg(c%nmod,idfn[b],idfn[b]+sz[b]-1,1,1,n);
break;
case 4:
cout<<que(idfn[b],idfn[b]+sz[b]-1,1,1,n)<<'\n';
break;
}
// printf("oper %d\n",i);
// pr(1,1,n);
}
return 0;
}

换根

换根对于链显然没有影响.

对于子树, 判断 \(\text {lca}(rt,u)\) 和 \(u\) 的关系. 发现换根后的子树为原子树/原子树的补集. 讨论之后同子树查询.

[模板] dfs序, 树链剖分, 换根的更多相关文章

  1. BZOJ 3083: 遥远的国度 dfs序,树链剖分,倍增

    今天再做一天树的题目,明天要开始专攻图论了.做图论十几天之后再把字符串搞搞,区域赛前再把计几看看. 3083: 遥远的国度 Time Limit: 10 Sec  Memory Limit: 128 ...

  2. 遥远的国度 (树链剖分换根),洛谷P3979

    析:显然,若没有换根操作,则为树链剖分板子题,但是这道题我们考虑换根操作 考虑这样一个性质:在一棵树上,两点的距离路径是唯一的!! 也就是说,我们在修改路径上的点权时,不必考虑根在哪里,直接利用模板修 ...

  3. 【树链剖分换根】P3979 遥远的国度

    Description zcwwzdjn在追杀十分sb的zhx,而zhx逃入了一个遥远的国度.当zcwwzdjn准备进入遥远的国度继续追杀时,守护神RapiD阻拦了zcwwzdjn的去路,他需要zcw ...

  4. HDU 3966 Aragorn's Story(模板题)【树链剖分】+【线段树】

    <题目链接> 题目大意: 给定一颗带点权的树,进行两种操作,一是给定树上一段路径,对其上每个点的点权增加或者减少一个数,二是对某个编号点的点权进行查询. 解题分析: 树链剖分的模板题,还不 ...

  5. BZOJ-3881:Divljak (AC自动机+DFS序+树链求并+树状数组)

    Alice有n个字符串S_1,S_2...S_n,Bob有一个字符串集合T,一开始集合是空的. 接下来会发生q个操作,操作有两种形式: “1 P”,Bob往自己的集合里添加了一个字符串P. “2 x” ...

  6. BZOJ3881[Coci2015]Divljak——AC自动机+树状数组+LCA+dfs序+树链的并

    题目描述 Alice有n个字符串S_1,S_2...S_n,Bob有一个字符串集合T,一开始集合是空的. 接下来会发生q个操作,操作有两种形式: “1 P”,Bob往自己的集合里添加了一个字符串P. ...

  7. BZOJ3991:寻宝游戏 (LCA+dfs序+树链求并+set)

    小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可以任意在地图的道路上行走 ...

  8. BZOJ-1036 树的统计Count 链剖线段树(模板)=(树链剖分+线段树)

    潇爷昨天刚刚讲完...感觉得还可以...对着模板打了个模板...还是不喜欢用指针.... 1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec Memory Lim ...

  9. 洛谷树剖模板题 P3384 | 树链剖分

    原题链接 对于以u为根的子树,后代节点的dfn显然比他的dfn大,我们可以记录一下回溯到u的dfn,显然这两个dfn构成了一个连续区间,代表u及u的子树 剩下的就和树剖一样了 #include< ...

随机推荐

  1. react 阻止事件冒泡

    前言 在学习react阻止事件冒泡,需要先了解 合成事件 和 原生事件 合成事件:在jsx中直接绑定的事件,就是合成事件: 原生事件: 通过js原生代码绑定的事件,就是原生事件: react事件:re ...

  2. Android 简单登陆 涉及 Button CheckBox TextView EditText简单应用

    GitHub地址:https://github.com/1165863642/LoginDemo 直接贴代码<?xml version="1.0" encoding=&quo ...

  3. Android 使用Picasso加载网络图片等比例缩放

    在做android图片加载的时候,由于手机屏幕受限,很多大图加载过来的时候,我们要求等比例缩放,比如按照固定的宽度,等比例缩放高度,使得图片的尺寸比例得到相应的缩放,但图片没有变形.显然按照andro ...

  4. 挖一挖MongoDB的备份与还原(实现指定时间点还原和增量备份还原)

    一  研究背景需求 目前作者所在公司的MongoDB数据库是每天凌晨做一次全库完整备份,但数据库出现故障时,只能保证恢复到全备时间点,比如,00:30 做的完整备份,而出现故障是下午18:00,那么现 ...

  5. centos7中mail实测(qq邮箱)

      一.安装mailx  sendmail 1.1查看是否已经安装       yum install -y  mailx  sendmail 如果已经安装,会提示已经安装,没有安装会自动安装.   ...

  6. SQLServer之存储过程简介

    存储过程定义 存储的过程 (存储过程(数据库引擎)) 是存储在数据库中的可执行对象. 存储过程分类 系统存储过程   系统存储过程是 SQL Server系统自身提供的存储过程,可以作为命令执行各种操 ...

  7. linux内核IDR机制详解【转】

    这几天在看Linux内核的IPC命名空间时候看到关于IDR的一些管理性质的东西,刚开始看有些迷茫,深入看下去豁然开朗的感觉,把一些心得输出共勉. 我们来看一下什么是IDR?IDR的作用是什么呢? 先来 ...

  8. Spring boot admin 节点状态一直为DOWN的排查

    项目中需要监控各个微服务节点的健康状态,找到了spring boot admin这个全家桶监控工具,它其实是Vue.js美化过的Spring Boot Actuator,官方的解释是: codecen ...

  9. Hadoop Yarn配置项 yarn.nodemanager.resource.local-dirs探讨

    1. What is the recommended value for "yarn.nodemanager.resource.local-dirs"? We only have ...

  10. python之并发编程

    一 背景知识 顾名思义,进程即正在执行的一个过程.进程是对正在运行程序的一个抽象. 进程的概念起源于操作系统,是操作系统最核心的概念,也是操作系统提供的最古老也是最重要的抽象概念之一.操作系统的其他所 ...