树链剖分 - Luogu 3384【模板】树链剖分
【模板】树链剖分
题目描述
已知一棵包含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的时间完成原问题中的操作。
剖分后的树有如下性质:
- 如果(v,u)为轻边,则siz[u] * 2 < siz[v];
- 从根到某一点的路径上轻链、重链的个数都不大于logn。
实现
这里我们用两遍大法师的一棵线段树版本
- 第一次,把sz,fa,mxch,dep求出来。在场的我相信你们都知道怎么写,实在不行复习一下树的遍历
- 第二次,对于每个节点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【模板】树链剖分的更多相关文章
- [luogu P3384] [模板]树链剖分
[luogu P3384] [模板]树链剖分 题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点 ...
- luoguP3384 [模板]树链剖分
luogu P3384 [模板]树链剖分 题目 #include<iostream> #include<cstdlib> #include<cstdio> #inc ...
- 【Luogu P3384】树链剖分模板
树链剖分的基本思想是把一棵树剖分成若干条链,再利用线段树等数据结构维护相关数据,可以非常暴力优雅地解决很多问题. 树链剖分中的几个基本概念: 重儿子:对于当前节点的所有儿子中,子树大小最大的一个儿子就 ...
- Luogu 2680 NOIP 2015 运输计划(树链剖分,LCA,树状数组,树的重心,二分,差分)
Luogu 2680 NOIP 2015 运输计划(树链剖分,LCA,树状数组,树的重心,二分,差分) Description L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之 ...
- Luogu 2590 [ZJOI2008]树的统计 / HYSBZ 1036 [ZJOI2008]树的统计Count (树链剖分,LCA,线段树)
Luogu 2590 [ZJOI2008]树的统计 / HYSBZ 1036 [ZJOI2008]树的统计Count (树链剖分,LCA,线段树) Description 一棵树上有n个节点,编号分别 ...
- 【算法学习】【洛谷】树链剖分 & P3384 【模板】树链剖分 P2146 软件包管理器
刚学的好玩算法,AC2题,非常开心. 其实很早就有教过,以前以为很难就没有学,现在发现其实很简单也很有用. 更重要的是我很好调试,两题都是几乎一遍过的. 介绍树链剖分前,先确保已经学会以下基本技巧: ...
- 算法笔记--树的直径 && 树形dp && 虚树 && 树分治 && 树上差分 && 树链剖分
树的直径: 利用了树的直径的一个性质:距某个点最远的叶子节点一定是树的某一条直径的端点. 先从任意一顶点a出发,bfs找到离它最远的一个叶子顶点b,然后再从b出发bfs找到离b最远的顶点c,那么b和c ...
- 【BZOJ】1036: [ZJOI2008]树的统计Count(lct/树链剖分)
http://www.lydsy.com/JudgeOnline/problem.php?id=1036 lct: (ps:为嘛我的那么慢T_T,不知道排到哪了..难道别人都是树剖吗...看来有必要学 ...
- hdu3966 点权模板-树链部分
Aragorn's Story Time Limit: 10000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) ...
随机推荐
- Java基础:(三)运算
一.参数传递 Java的参数是以值传递的形式传入方法中,而不是引用传递. 二.隐式类型转换 Java不能隐式执行向下转型,因为这会使精度降低:但是使用 += 运算符可以执行隐式类型转换. float ...
- hystrix 给方法加断路器
添加依赖 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>s ...
- 带你零基础入门redis【二】
本篇文章介绍redis如何设置开机自启动以及如何在java中应用 一.设置redis开机自启 1.修改redis配置 [root@VM_6_102_centos ~]# vim /usr/local/ ...
- 初识SeekBar
SeekBar拖动条,是Progress的间接子类 <SeekBar android:id="@+id/seekBar1" android:layout_width=&quo ...
- Elasticsearch-基本操作2
Elasticsearch版本:6.0 为了避免并发修改的冲突问题,数据库中,经常用悲观锁和乐观锁来控制并发问题,而Elasticsearch使用乐观锁.如果源数据在读写过程中被修改,更新将失败,应用 ...
- [VC]在VC++中实现让程序只运行一个实例的方法且实现该实例
方法一: 有时候在开发应用程序时,希望控制程序运行唯一的实例.例如,最常用的mp3播放软 件Winamp,由于它需要独占计算机中的音频设备,因此该程序只允许自身运行唯一的一个例程.在Visual C+ ...
- UVALive 3026 Period (KMP算法简介)
kmp的代码很短,但是不太容易理解,还是先说明一下这个算法过程吧. 朴素的字符串匹配大家都懂,但是效率不高,原因在哪里? 匹配过程没有充分利用已经匹配好的模版的信息,比如说, i是文本串当前字符的下标 ...
- Flash as3.0 保存MovieClip运动轨迹到json文件
//放在第一帧调用 import flash.events.Event; import flash.display.MovieClip; stage.addEventListener(Event.EN ...
- BZOJ-3679(数位DP)
#include <bits/stdc++.h> using namespace std; typedef long long ll; ll a,b; int k[20]; ll dp[2 ...
- [LUOGU]P1508 Likecloud-吃、吃、吃
题目背景 问世间,青春期为何物? 答曰:"甲亢,甲亢,再甲亢:挨饿,挨饿,再挨饿!" 题目描述 正处在某一特定时期之中的李大水牛由于消化系统比较发达,最近一直处在饥饿的状态中.某日 ...