【模板】树链剖分

题目描述

已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作:

操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z

操作2: 格式: 2 x y 表示求树从x到y结点最短路径上所有节点的值之和

操作3: 格式: 3 x z 表示将以x为根节点的子树内所有节点值都加上z

操作4: 格式: 4 x 表示求以x为根节点的子树内所有节点值之和


树链剖分模板题废话

然而事实上我之前一直都不会树链剖分(真不知道我怎么活到现在的)

听说树链剖分有维护一棵线段树和多棵的版本

但是这里因为要维护子树其实是懒得写另一种,用两遍dfs的一棵线段树版本

先明确一下概念

重儿子:点v的子节点中sz值最大的儿子。

轻儿子:v的其它子节点。

重边: 点v与其重儿子的连边。

轻边:点v与其轻儿子的连边。

重链:由重边连成的路径。

轻链:有轻边的路径。

树链剖分概述

树链,就是树上的路径。

剖分,就是把路径分类为重链和轻链。

记sz[v]表示以v为根的子树的节点数,

dep[v]表示v的深度,

top[v]表示v所在的重链的顶端节点,

fa[v]表示v的父亲,

mxch表示与v在同一重链上的v的儿子节点,

loc[v]表示v与其父亲节点的连边在线段树中的位置。

只要把这些东西求出来,就能用logn的时间完成原问题中的操作。

剖分后的树有如下性质:

  1. 如果(v,u)为轻边,则siz[u] * 2 < siz[v];
  2. 从根到某一点的路径上轻链、重链的个数都不大于logn。

实现

这里我们用两遍大法师的一棵线段树版本

  1. 第一次,把sz,fa,mxch,dep求出来。在场的我相信你们都知道怎么写,实在不行复习一下树的遍历
  2. 第二次,对于每个节点v,

    2.1. 当v不是叶子节点,有top[mxch] = top[v]。

    线段树中,v的重边应当在v的父边的后面,记loc[mxch] = las+1,las表示到现在为止最后加入的一条边在线段树中的位置。

    此时,为了使一条重链各边在线段树中连续分布,应当进行DFS2(mxch);

    2.2. 对于v的各个轻儿子u,显然有top[u] = u,并且loc[u] = las+1,DFS2它。

之后将树中各边的权值sei进线段树,建树链和建线段树的过程就完成了。

想了解更多,去GZY大佬推荐的博客 http://blog.sina.com.cn/s/blog_6974c8b20100zc61.html .


代码蒯上

可能因为博主咸鱼,有些地方看起来不和谐,请谅解。

#include<iostream>
#include<iomanip>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
inline int gotcha()
{
register int _a=0;bool _b=1;register char _c=getchar();
while(_c<'0' || _c>'9'){if(_c=='-')_b=0;_c=getchar();}
while(_c>='0' && _c<='9')_a=_a*10+_c-48,_c=getchar();
return _b?_a:-_a;
}
const int _ = 200002;
struct tree{int l,r,v,lzy;}tr[4*_];
struct edge{int to,ne;edge(){to=ne=0;}}eg[2*_];
int he[_]={0},io[_]={0},ecnt=0;
void add(int fr,int to)
{io[fr]++;eg[++ecnt].to=to;eg[ecnt].ne=he[fr];he[fr]=ecnt;}
int w[_],num[_],e[_],top[_],rnk[_],sz[_],dep[_],fa[_],n,m,mo,s,tot,z;
void plant(int d,int l,int r)
{
int mid=(l+r)>>1;
tr[d].l=l,tr[d].r=r;
if(l==r)return;
plant(d<<1,l,mid),plant(d<<1|1,mid+1,r);
}
void down(int d)
{
if(!tr[d].lzy)return;
tr[d].v+=tr[d].lzy*(tr[d].r-tr[d].l+1)%mo;
tr[d<<1].lzy+=tr[d].lzy;tr[d<<1|1].lzy+=tr[d].lzy;
tr[d].lzy=0;
}
void change(int d,int l,int r,int add)
{
if(tr[d].l>r||tr[d].r<l)return;
if(tr[d].l>=l && tr[d].r<=r){tr[d].lzy+=add,down(d);return;}
change(d<<1,l,r,add),change(d<<1|1,l,r,add),down(d<<1),down(d<<1|1);
tr[d].v=(tr[d<<1].v+tr[d<<1|1].v)%mo;
}
int finder(int d,int l,int r)
{
if(tr[d].l>r||tr[d].r<l)return 0;
if(tr[d].l>=l && tr[d].r<=r)return (tr[d].v)%mo;
down(d<<1);down(d<<1|1);
return (finder(d<<1,l,r)+finder(d<<1|1,l,r))%mo;
}
void inv_change(int x,int y,int add)
{
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])swap(x,y);
change(1,num[top[x]],num[x],add);
x=fa[top[x]];
}
if(num[x]>num[y])swap(x,y);change(1,num[x],num[y],add);
}
int sp_finder(int x,int y)
{
int ans=0;
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])swap(x,y);
(ans+=finder(1,num[top[x]],num[x]))%=mo;
x=fa[top[x]];
}
if(num[x]>num[y])swap(x,y);
(ans+=finder(1,num[x],num[y]))%=mo;
return ans;
}
void DFS(int d,int faa)
{
int i=0;sz[d]=1;dep[d]=dep[faa]+1;
fa[d]=faa;
for(i=he[d];i;i=eg[i].ne)
if(eg[i].to!=faa)DFS(eg[i].to,d),sz[d]+=sz[eg[i].to];
}
void DFS2(int d,int head)
{
int i,mxch=0;
top[d]=head;num[d]=++tot;rnk[num[d]]=tot;change(1,tot,tot,w[d]);
if(io[d]==1 && d!=s){e[d]=d;return;}
for(i=he[d];i;i=eg[i].ne)
if(sz[eg[i].to]<sz[d] && sz[eg[i].to]>sz[mxch])mxch=eg[i].to;
DFS2(mxch,head);e[d]=e[mxch];
for(i=he[d];i;i=eg[i].ne)
if(sz[eg[i].to]<sz[d] && eg[i].to!=mxch)
DFS2(eg[i].to,eg[i].to),e[d]=e[eg[i].to];
}
int main()
{
register int i,op,a,b,c;
n=gotcha(),m=gotcha(),s=gotcha(),mo=gotcha();
for(i=1;i<=n;i++)w[i]=gotcha();
for(i=1;i<=n-1;i++)a=gotcha(),b=gotcha(),add(a,b),add(b,a);
plant(1,1,n),DFS(s,0),DFS2(s,s);
for(i=1;i<=m;i++)
{
op=gotcha(),a=gotcha();
if(op==1)b=gotcha(),c=gotcha(),inv_change(a,b,c);
else if(op==2)b=gotcha(),printf("%d\n",sp_finder(a,b)%mo);
else if(op==3)b=gotcha(),change(1,num[a],num[e[a]],b);
else printf("%d\n",finder(1,num[a],num[e[a]])%mo);
}
return 0;
}

树链剖分 - Luogu 3384【模板】树链剖分的更多相关文章

  1. [luogu P3384] [模板]树链剖分

    [luogu P3384] [模板]树链剖分 题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点 ...

  2. luoguP3384 [模板]树链剖分

    luogu P3384 [模板]树链剖分 题目 #include<iostream> #include<cstdlib> #include<cstdio> #inc ...

  3. 【Luogu P3384】树链剖分模板

    树链剖分的基本思想是把一棵树剖分成若干条链,再利用线段树等数据结构维护相关数据,可以非常暴力优雅地解决很多问题. 树链剖分中的几个基本概念: 重儿子:对于当前节点的所有儿子中,子树大小最大的一个儿子就 ...

  4. Luogu 2680 NOIP 2015 运输计划(树链剖分,LCA,树状数组,树的重心,二分,差分)

    Luogu 2680 NOIP 2015 运输计划(树链剖分,LCA,树状数组,树的重心,二分,差分) Description L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之 ...

  5. Luogu 2590 [ZJOI2008]树的统计 / HYSBZ 1036 [ZJOI2008]树的统计Count (树链剖分,LCA,线段树)

    Luogu 2590 [ZJOI2008]树的统计 / HYSBZ 1036 [ZJOI2008]树的统计Count (树链剖分,LCA,线段树) Description 一棵树上有n个节点,编号分别 ...

  6. 【算法学习】【洛谷】树链剖分 & P3384 【模板】树链剖分 P2146 软件包管理器

    刚学的好玩算法,AC2题,非常开心. 其实很早就有教过,以前以为很难就没有学,现在发现其实很简单也很有用. 更重要的是我很好调试,两题都是几乎一遍过的. 介绍树链剖分前,先确保已经学会以下基本技巧: ...

  7. 算法笔记--树的直径 && 树形dp && 虚树 && 树分治 && 树上差分 && 树链剖分

    树的直径: 利用了树的直径的一个性质:距某个点最远的叶子节点一定是树的某一条直径的端点. 先从任意一顶点a出发,bfs找到离它最远的一个叶子顶点b,然后再从b出发bfs找到离b最远的顶点c,那么b和c ...

  8. 【BZOJ】1036: [ZJOI2008]树的统计Count(lct/树链剖分)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1036 lct: (ps:为嘛我的那么慢T_T,不知道排到哪了..难道别人都是树剖吗...看来有必要学 ...

  9. hdu3966 点权模板-树链部分

    Aragorn's Story Time Limit: 10000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...

随机推荐

  1. 常用API(正则表达式、Date、DateFormat、Calendar)

    常用API 今日内容介绍 u 正则表达式 u Date u DateFormat u Calendar 第1章 正则表达式 1.1 正则表达式的概念 正则表达式(英语:Regular Expressi ...

  2. w3c万维网的介绍和html基本构成

    怎么与浏览器交互? 1.鼠标 2.键盘输入 w3c标准: 中文名:万维网联盟!外文名:world wide web cansortium万维网联盟创建于1994年,是web技术领域最具权威个影响的国际 ...

  3. JFinal免费公开课更新中

    价值千元的课程,免费报名学习,JFinal学院-小木 录制JFinal视频教程,JFinal核心已经周边涉及到微信小程序开发.数据库.前端实战等.

  4. [Oracle 视图] ALL_OBJECTS

    ALL_OBJECTS ALL_OBJECTS describes all objects accessible to the current user. ALL_OBJECTS描述当前用户的可访问的 ...

  5. SqlServer 填充因子的说明

    CREATE NONCLUSTERED INDEX IX_d_name ON department(d_name) with fillfactor=30 使用 fill factor 选项可以指定 M ...

  6. 使用脚本在Linux服务器上自动安装Kubernetes的包管理器Helm

    Helm之于Kubernetes好比yum之于Red Hat Enterprise Linux,或者apt-get之于Ubuntu. Helm是由helm CLI和Tiller组成,是典型的Clien ...

  7. Contour Features 边界特征

    查找轮廓 findContours   cv2.findContours(image, mode, method[, contours[, hierarchy[, offset]]]) → image ...

  8. SQLServer锁原理和锁的类型

    1.锁的用途 为了避免同时争夺数据库资源,将数据库加锁,只有拿到钥匙的用户才能使用: 2.锁的粒度 行锁(Row)--->页锁(Page)--->区锁(Partition 8个页)---- ...

  9. 数据结构C语言实现系列——线性表(单向链表)

    #include <stdio.h> #include <stdlib.h> #define NN 12 #define MM 20 typedef int elemType ...

  10. 初涉斯坦纳树&&bzoj4774: 修路

    斯坦纳树的基础应用 斯坦纳树有什么用 个人一点粗浅理解…… 最基本形式的斯坦纳树问题(以下简称母问题):给定图G和一个关键点集V.求在G中选取一个权值最小(这里权值可以有很多变式)的边集E使V中的点两 ...