树链剖分

树链剖分是一种对树的分治, 可以把树上的任意一条链分解为 \(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. 浅谈"n个球"和"m个盒子"之间的乱伦关系

    无视标题,从我做起 update in 2018.10.1: 补充了"至多为1的四中情况" 这玩意儿的官方名字应该是叫"Twelvefold way",共用12 ...

  2. 利用MingW检验程序运行内存

    今天zhx老师在讲课的时候提到了一种检验程序内存的方法 一般计算内存的方法就是手算,手动计算代码中每个变量所占的内存然后加起来 具体可以参考这篇文章 zhx老师讲的方法可以实现全自动化计算内存 具体怎 ...

  3. 【阿里云】在 Windows Server 2016 下使用 FileZilla Server 安装搭建 FTP 服务

     Windows Server 2016 下使用 FileZilla Server 安装搭建 FTP 服务 一.安装 Filezilla Server 下载最新版本的 Filezilla Server ...

  4. Android 简单实现控件的拖动

    控件的拖动,使用到一个监听事件 setOnTouchListener:XML代码: <?xml version="1.0" encoding="utf-8" ...

  5. 新版的nuget包 PackageLicense 这样写

    Intro 最近编译类库项目的时候发现总是有个 licenseUrl 的警告,警告信息如下: warning NU5125: The 'licenseUrl' element will be depr ...

  6. Nodejs 操作 Sql Server

    Nodejs 操作 Sql Server Intro 最近项目需要爬取一些数据,数据有加密,前端的js又被混淆了,ajax请求被 hook 了,有些复杂,最后打算使用 puppeteer 来爬取数据. ...

  7. c/c++ linux epoll系列2 利用epoll_wait查看是否可以送信

    linux epoll系列2 利用epoll_wait查看是否可以送信 write函数本来是非阻塞函数,但是当缓存区被写满后,再往缓存区里写的时候,就必须等待缓存区再次变成可写,所以这是write就变 ...

  8. sqlserver中批量导出所有作业或链接脚本

    问题描述: 经常在数据库迁移到另外一台服务器的时候,需要把作业也迁移过去,但是作业有时候好多个,要是一个个编写监本出来很麻烦 今天知道个简单方法批量可以导出sql脚本,顺便做个笔记 解决方法: 1.在 ...

  9. Bootstrap -- 缩略图、进度条、列表组、面板

    Bootstrap -- 缩略图.进度条.列表组.面板 1. 缩略图 大多数站点都需要在网格中布局图像.视频.文本等.Bootstrap 通过缩略图为此提供了一种简便的方式.使用 Bootstrap ...

  10. Allowed memory size of 134217728 bytes exhausted解决办法(php内存耗尽报错)【简记】

    报错: PHP Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 72 bytes) i ...