树链剖分( 洛谷P3384 )
我们有时候遇到这样一类题目,让我们维护树上路径的某些信息,这个时候发现我们无法用线段树或者树状数组来维护这些信息,那么我们就有着一种新的数据结构,树剖:将一棵树划分成若干条链,用数据结构去维护每条链,复杂度为O(logN)。
剖分方法:
盲目剖分
随机剖分
启发式剖分
综合比较,启发式剖分是剖分时的最佳选择。
将树中的边分为:轻边和重边
定义size(X)为以X为根的子树的节点个数。
令V为U的儿子节点中size值最大的节点,那么边(U,V)被称为重边,树中重边之外的边被称为轻边。
轻重边路径剖分的性质
轻边(U,V),size(V)<=size(U)/2。
从根到某一点的路径上,不超过O(logN)条轻边,不超过O(logN)条重路径。
重链剖分
重链剖分的过程为2次DFS
第一次:找重边
第二次:连重边成重链
- 找重边 一次DFS,可记下所有的重边。
- 连重边成重链,以根节点为起点,沿重边向下拓展,拉成重链。不在当前重链上的节点,都以该节点为起点向下重新拉一条重链。
剖分完之后,每条重链就相当于一段区间,用数据结构去维护。
把所有的重链首尾相接,放到同一个数据结构上,然后维护这一个整体即可。
修改操作
单独修改一个点的权值
根据新的编号直接在数据结构中修改就行了。
修改操作 整体修改点 U和点V的路径上的权值
- 如果U和V在同一条重链上 : 直接用数据结构修改tid[U]至tid[V]间的值。
- 如果U和V不在同一条重链上一边进行修改,一边将U和V往同一条重链上靠,然后就变成了I的情况。
怎样把他们向一起靠?
A.若fa[top[U]]与V在同一条重链上。
修改点U与top[u]间的各权值,然后U跳至fa[top[u],就变成了I的情况。
B.若U向上经过若干条重链和轻边与V在同一条重链上。
不断地修改当前U和top[u]间的各权值,再将U跳至fa[top[U]],直到U与V在同一条重链。
C.若U和V都是向上经过若干条重链和轻边,到达同一条重链。
每次在点U和点V中,选择dep[top[x]]较大的点x,修改x与top[x]间的各权值,再跳至fa[top[x]],直到点U和点V在同一条重链。
情况A、B是情况C的比较特殊的2种。
I也只是II的特殊情况。
所以,这一操作只要用一个过程。
下附代码:
#include<bits/stdc++.h>
#define sight(c) ('0'<=c&&c<='9')
#define LL int
#define gc nc
#define L(x) (x&-x)
#define eho(x) for(int i=head[x];i;i=net[i])
#define N 200007
LL q1[N],q2[N],gg,sum[N],a[N],T,G;
int tot,fall[N<<],net[N<<],head[N],top[N],son[N],f[N],dp[N],siz[N],mo,be[N],ed[N],ok
,n,m,A,B,t[N],op,x,y,z,dla,OS;
inline char nc(){
static char buf[],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,,,stdin),p1==p2)?EOF:*p1++;
}
inline void swap(int &x,int &y) {x^=y; y^=x; x^=y;}
inline void read(LL &x){
static char c;
for(c=gc();!sight(c);c=gc());
for(x=;sight(c);c=gc()) x=x*+c-;
}
void write(LL x){if (x<) {putchar(''+x);return;}write(x/); putchar(''+x%);}
inline void ADd(int x,int y) {
fall[++tot]=y; net[tot]=head[x]; head[x]=tot;
}
inline void add(LL &x,LL y) {
x=x+y; if (x>=mo) x=x%mo; if (x<) x=x%mo+mo;
}
inline void Add(LL *A,int x,int dla) {for (;x<N;x+=L(x)) add(A[x],dla);}
inline void adds(int l,int r,int x) {
Add(q1,l,x); Add(q1,r+,-x); Add(q2,l,l*x%mo); Add(q2,r+,-(r+)*x%mo);
}
inline LL Query(LL *A,int x){for(G=;x;x-=L(x)) add(G,A[x]);return G;}
inline LL qurey(int l){
return (sum[l]+(l+)*1ll*Query(q1,l)-Query(q2,l))%mo;
}
void dfs(int x,int fa){
siz[x]=; son[x]=-; dp[x]=dp[fa]+; f[x]=fa;
eho(x) if (fall[i]^fa) {
dfs(fall[i],x); siz[x]+=siz[fall[i]];
if ((!(~son[x]))||siz[fall[i]]>siz[son[x]]) son[x]=fall[i];
}
}
void dfs2(int x,int las){
t[++ok]=x;top[x]=las; be[x]=ok;
if (~son[x]) dfs2(son[x],las);
eho(x) if ((fall[i]^f[x])&&(fall[i]^son[x])) dfs2(fall[i],fall[i]); ed[x]=ok;
}
void apd(int x,int y,LL dla){
while (top[x]!=top[y]) {
if (dp[top[x]]<dp[top[y]]) swap(x,y);
adds(be[top[x]],be[x],dla);
x=f[top[x]];
} if (dp[x]>dp[y]) swap(x,y);
adds(be[x],be[y],dla);
}
LL query_path(int x,int y) {
LL O=;
while (top[x]!=top[y]) {
if (dp[top[x]]<dp[top[y]]) swap(x,y);
add(O,qurey(be[x])-qurey(be[top[x]]-));
x=f[top[x]];
} if (dp[x]>dp[y]) swap(x,y);
add(O,qurey(be[y])-qurey(be[x]-));
return O;
}
int main () {
read(n); read(m); read(OS);read(mo);
for (int i=;i<=n;i++) read(a[i]);
for (int i=;i< n;i++) {read(A); read(B); ADd(A,B); ADd(B,A); }
dfs(OS,); dfs2(OS,OS);
for (int i=;i<=n;i++) sum[i]=sum[i-],add(sum[i],a[t[i]]);
while (m--) {
read(op);
switch (op) {
case : read(x),read(y),read(dla); apd(x,y,dla); break;
case : read(x),read(y); write(query_path(x,y));putchar('\n'); break;
case : read(x); read(z); adds(be[x],ed[x],z); break;
case : read(x); T=qurey(ed[x])-qurey(be[x]-); add(T,0ll);
write(T); putchar('\n');break;
}
} return ;
}
我用了树状数组维护区间,因为这样比较方便。
不懂的同学点这里:传送门
树链剖分( 洛谷P3384 )的更多相关文章
- AC日记——【模板】树链剖分 洛谷 P3384
题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式 ...
- 树链剖分 (求LCA,第K祖先,轻重链剖分、长链剖分)
2020/4/30 15:55 树链剖分是一种十分实用的树的方法,用来处理LCA等祖先问题,以及对一棵树上的节点进行批量修改.权值和查询等有奇效. So, what is 树链剖分? 可以简单 ...
- 树链剖分模板(洛谷P3384)
洛谷P3384 #include <bits/stdc++.h> #define DBG(x) cerr << #x << " = " < ...
- 【算法学习】【洛谷】树链剖分 & P3384 【模板】树链剖分 P2146 软件包管理器
刚学的好玩算法,AC2题,非常开心. 其实很早就有教过,以前以为很难就没有学,现在发现其实很简单也很有用. 更重要的是我很好调试,两题都是几乎一遍过的. 介绍树链剖分前,先确保已经学会以下基本技巧: ...
- 洛谷 P3384 【模板】树链剖分-树链剖分(点权)(路径节点更新、路径求和、子树节点更新、子树求和)模板-备注结合一下以前写的题目,懒得写很详细的注释
P3384 [模板]树链剖分 题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节 ...
- 洛谷p3384【模板】树链剖分题解
洛谷p3384 [模板]树链剖分错误记录 首先感谢\(lfd\)在课上调了出来\(Orz\) \(1\).以后少写全局变量 \(2\).线段树递归的时候最好把左右区间一起传 \(3\).写\(dfs\ ...
- 洛谷 P3384 【模板】树链剖分
树链剖分 将一棵树的每个节点到它所有子节点中子树和(所包含的点的个数)最大的那个子节点的这条边标记为"重边". 将其他的边标记为"轻边". 若果一个非根节点的子 ...
- 树链剖分详解(洛谷模板 P3384)
洛谷·[模板]树链剖分 写在前面 首先,在学树链剖分之前最好先把 LCA.树形DP.DFS序 这三个知识点学了 emm还有必备的 链式前向星.线段树 也要先学了. 如果这三个知识点没掌握好的话,树链剖 ...
- 洛谷P3384 树链剖分
如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式: 2 x ...
- 洛谷 P3384 树链剖分(模板题)
题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式 ...
随机推荐
- 蓝桥杯 密码脱落 LCS
9. 密码脱落(后来题目说是有问题,测试用例不会有E出现) X星球的考古学家发现了一批古代留下来的密码. 这些密码是由A.B.C.D 四种植物的种子串成的序列. 仔细分析发现,这些密码串当初应该是前后 ...
- javascript设计模式——单例模式
前面的话 单例模式是指保证一个类仅有一个实例,并提供一个访问它的全局访问点. 单例模式是一种常用的模式,有一些对象往往只需要一个,比如线程池.全局缓存.浏览器中的window对象等.在javaScri ...
- 40.Linux应用调试-使用gdb和gdbserver
1.gdb和gdbserver调试原理 通过linux虚拟机里的gdb,来向开发板里的gdbserver发送命令,比如设置断点,运行setp等,然后开发板上的gdbserver收到命令后,便会执行应用 ...
- vs 2015工具栏添加Tab Order
1. 在工具栏右键,弹出菜单,选中“Customize”菜单项. 2. 选中Commands标签页,选择Toolbar,选择自己要加入的Tab order的类别,之后点击“Add Command”按钮 ...
- 200行Java代码搞定计算器程序
发现了大学时候写的计算器小程序,还有个图形界面,能够图形化展示表达式语法树,哈哈;) 只有200行Java代码,不但能够计算加减乘除,还能够匹配小括号~ 代码点评: 从朴素的界面配色到简单易懂错误提示 ...
- 逐步搭建Lamp环境之Linux的运行模式
首先先来看几个概念,分别是:单用户.单任务.多用户.多任务 单用户: 是指操作系统一般只能由一个人同时进行登录 单任务: 是指操作系统只能同时处理一个任务 多用户: 是指操作系统可以允许由多个用户同时 ...
- 直接编译caffe出现的两个问题
工控机的环境之前已经配置好ubuntu14.04+CUDA7.5+cuDNN v4,再加opencv3.1.要用ResNet做分类,需要重新编译一个caffe框架.下载BVLC/caffe,接着修改M ...
- Java基础—标识符及命名规范
什么是标识符符? 凡是可以由自己命名的地方都称为修饰符. 例: 项目名 ,包名 ,类名 .方法名 2. 命名规范. ① 不可使用java关键字和保留字,但是可以包含关键字和保留字. ② ...
- 解析XML文件之使用SAM解析器
XML是一种常见的传输数据方式,所以在开发中,我们会遇到对XML文件进行解析的时候,本篇主要介绍使用SAM解析器,对XML文件进行解析. SAX解析器的长处是显而易见的.那就是SAX并不须要将全部的文 ...
- 基于QT的异质链表实例
所谓的异质链表就是的节点元素类型能够不同.本实例採用C++抽象类和多态实现. #include <QApplication> #include<QPushButton> #in ...