树链剖分 - 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) ...
随机推荐
- css3背景与边框相关样式
background-attachment 背景图像是否固定或者随着页面的其余部分滚动 background-color 设置元素的背景颜色 b ...
- If you can't take it, don't dish it out.
If you can't take it, don't dish it out.己所不欲,勿施于人.
- 零基础逆向工程16_C语言10_宏定义_头文件_内存分配_文件读写
#define 无参数的宏定义的一般形式为:#define 标识符 字符序列 如:#define TRUE 1 注意事项: 1.之作字符序列的替换工作,不作任何语法的检查 2.如果宏定义不当,错误要到 ...
- 科学效法自然:微软研究人员测试AI控制的滑翔机
编者按:正如一颗苹果砸出了万有引力,自然界所有存在的事物和现象都有其科学合理的一面,小小的鸟儿也能够给科学带来丰富的灵感和启示. 最近,微软研究人员从自然出发,研究鸟类能够自由停留在空中的科学原理,并 ...
- mysql-单表操作
mySql单表操作主要有一下内容: 1.查询:查询又分为几种,范围查询,模糊查询.空值查询.多条件查询 查询的语句格式为:SELECT 字段 1,字段 2,字段 3...FROM 表名 WHERE 条 ...
- jsp之初识UserBean
package com.java.model; public class Student { private String name; private int age; public String g ...
- 让您的Eclipse具有千变万化的外观
大家每天用Eclipse做Java开发,是否厌倦了Eclipse千篇一律的白色背景呢? 看看Jerry这几种不同风格的Eclipse外观,是不是有耳目一新的感觉?如何做到的? 需要给Eclipse安装 ...
- python-下拉框处理
在自动化中python对下拉框的处理网上相对实例比较少,其它前辈写的教程中对下拉也仅仅是相对与教程来说的,比如下面: m=driver.find_element_by_id("Shippin ...
- Unity3d 中键值监听方法
unity3d的api中没有负责监听键值的方法,不过unity的input类是通过c#类获取各类监听事件,所以我们可以通过c#类监听,方法如下: void OnGUI() { Event e = Ev ...
- CF-1140 E - Palindrome-less Arrays
题意:给定一个没有填完的序列,数值为-1表示你可以用 1~k 中的数字去覆盖它,求将该序列填充后,不存在长度为奇数的回文串的方案数 分析: 使之不存在长度为奇数的回文串,只需要满足不存在长度为3的回文 ...