树链剖分 - 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) ...
随机推荐
- pixhawk在linux(ubuntu16.04)下的开发环境搭建和源码编译
1查找安装文档(http://dev.px4.io/starting-installing-linux.html)(本文仅针对硬件为PIXHAWK的开发环境搭建,其他硬件请参考官方文档) ...
- Android自定义组件系列【17】——教你如何高仿微信录音Toast
一.Toast介绍 平时我们在Android开发中会经常用到一个叫Toast的东西,官方解释如下 A toast is a view containing a quick little message ...
- Windows Azure 配置Active Directory 主机(2)
前一篇概况给大家介绍了,在云端部署一台DC 需要满足一些条件,接下来进入正题,云端VM安装域控制器具体步骤. 步骤1 :验证 主DC 的静态 IP 地址 1.登录到 Corp 网络上的 主DC. 2. ...
- SqlServer中生成一串连续数字
在SQLServer中一串连续数字,如1,2,3,4,5,....或者 1 2 3 4 5 没有现成方法,网上都用通用表表达式递归生成.今天想到一个还算简单的方法,记录下来: select row_n ...
- javaSe-反射1
package com.java.chap07.sec01; public class Student { private String name; private Integer age; //创建 ...
- 【Python图像特征的音乐序列生成】关于数据集的分享和样例数据
数据集还在制作中,样例数据如下: 我将一条数据作为一行,X是ID,O代表了情感向量,S是速度,是一个很关键的参数,K是调式,M是节拍,L是基本拍.后面是ABC格式的序列,通过embedding化这些音 ...
- C基础的练习集及测试答案(31-39)
31.读懂以下程序,说明程序的功能#include<stdio.h>int main(){ int m,n,r,m1,m2; printf("请输入2个正整数:"); ...
- 使用HelpProvide组件调用帮助文件
实现效果: 知识运用: HelpProvider组件的HelpNameSpace属性 //于对象关联的帮助文件名 public virtual string HelpNameSpace {get; s ...
- oracle 快速复制一张表,并在此创建索引,日志及并行度
复制表结构及其数据 create table table_name_new as select * from table_name_old 只复制表结构 create table table_name ...
- 什么是静态代码块?java中如何使用空参构造方法自动生成不同名字的对象,使用非静态的属性和静态属性有什么区别,原因是什么?如何理解static关键字
静态代码块?类加载就执行,最先执行 class demo{ static int num; static{ num=10; num*=3; System.out.println("haha& ...