题目传送门

树链剖分

题目描述

这是一道模板题。

给定一棵 n 个节点的树,初始时该树的根为 1 号节点,每个节点有一个给定的权值。下面依次进行 m 个操作,操作分为如下五种类型:

  • 换根:将一个指定的节点设置为树的新根。

  • 修改路径权值:给定两个节点,将这两个节点间路径上的所有节点权值(含这两个节点)增加一个给定的值。

  • 修改子树权值:给定一个节点,将以该节点为根的子树内的所有节点权值增加一个给定的值。

  • 询问路径:询问某条路径上节点的权值和。

  • 询问子树:询问某个子树内节点的权值和。

输入格式

第一行为一个整数 n,表示节点的个数。

第二行 nn个整数表示第 i 个节点的初始权值 a​i​​。

第三行 n−1 个整数,表示i+1 号节点的父节点编号 $f_{i+1}\ (1 \leqslant f_{i+1} \leqslant n)$。

第四行一个整数 m,表示操作个数。

接下来 m 行,每行第一个整数表示操作类型编号:$(1 \leqslant u, v \leqslant n)$

  • 若类型为 1,则接下来一个整数 u,表示新根的编号。

  • 若类型为 2,则接下来三个整数 u,v,k,分别表示路径两端的节点编号以及增加的权值。

  • 若类型为 3,则接下来两个整数 u,k,分别表示子树根节点编号以及增加的权值。

  • 若类型为 4,则接下来两个整数 u,v,表示路径两端的节点编号。

  • 若类型为 5,则接下来一个整数 u,表示子树根节点编号。

输出格式

对于每一个类型为 4 或 5 的操作,输出一行一个整数表示答案。

样例

样例输入

6
1 2 3 4 5 6
1 2 1 4 4
6
4 5 6
2 2 4 1
5 1
1 4
3 1 2
4 2 5

样例输出

15
24
19

数据范围与提示

对于 100% 的数据,$1\leqslant n,m,k,a_i\leqslant 10^5$​。数据有一定梯度。


  分析:

  虽然说是树剖模板,但是这个换根操作确实清奇。。。在集训的时候遇到了这题,当时反正是弃疗了。。。实际上换根操作并没有什么影响,在修改查询的时候判断一下就可以了。

  Code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+;
int n,m,root,a[N],head[N],cnt,fa[N],dep[N];
int top[N],dfn[N],num[N],id,size[N],hson[N];
struct Node{int to,next;}edge[N<<];
struct Seg{
ll seg[N<<],sign[N<<];
void pushup(int rt)
{
seg[rt]=seg[rt<<]+seg[rt<<|];
}
void pushdown(int l,int r,int rt)
{
if(!sign[rt])return;
int mid=(l+r)>>;
seg[rt<<]+=sign[rt]*(mid-l+);
seg[rt<<|]+=sign[rt]*(r-mid);
sign[rt<<]+=sign[rt];
sign[rt<<|]+=sign[rt];
sign[rt]=;
}
void build(int l,int r,int rt)
{
if(l>r)return;
if(l==r){
seg[rt]=a[dfn[l]];return;}
int mid=(l+r)>>;
build(l,mid,rt<<);
build(mid+,r,rt<<|);
pushup(rt);
}
void update(int l,int r,int rt,int L,int R,ll C)
{
if(l>R||r<L)return;
if(L<=l&&r<=R){
seg[rt]+=(r-l+)*C;sign[rt]+=C;return;}
int mid=(l+r)>>;
pushdown(l,r,rt);
if(L<=mid)update(l,mid,rt<<,L,R,C);
if(R>mid)update(mid+,r,rt<<|,L,R,C);
pushup(rt);
}
ll quary(int l,int r,int rt,int L,int R)
{
if(l>R||r<L)return ;
if(L<=l&&r<=R){return seg[rt];}
int mid=(l+r)>>;ll ret=;
pushdown(l,r,rt);
if(L<=mid)ret+=quary(l,mid,rt<<,L,R);
if(R>mid)ret+=quary(mid+,r,rt<<|,L,R);
return ret;
}
}T;
inline int read()
{
char ch=getchar();int num=;bool flag=false;
while(ch<''||ch>''){if(ch=='-')flag=true;ch=getchar();}
while(ch>=''&&ch<=''){num=num*+ch-'';ch=getchar();}
return flag?-num:num;
}
inline void add(int x,int y)
{
edge[++cnt].to=y;
edge[cnt].next=head[x];
head[x]=cnt;
}
inline void dfs1(int u)
{
size[u]=;
for(int i=head[u];i!=-;i=edge[i].next){
int v=edge[i].to;if(v==fa[u])continue;
fa[v]=u;dep[v]=dep[u]+;
dfs1(v);size[u]+=size[v];
if(size[v]>size[hson[u]])hson[u]=v;
}
}
inline void dfs2(int u,int now)
{
top[u]=now;num[u]=++id;dfn[id]=u;
if(!hson[u])return;dfs2(hson[u],now);
for(int i=head[u];i!=-;i=edge[i].next){
int v=edge[i].to;
if(v==fa[u]||v==hson[u])continue;
dfs2(v,v);}
}
inline int check(int x)
{
if(x==root)return -;
if(!(num[x]<num[root]&&num[x]+size[x]->=num[root]))return ;
int now=root;
while(dep[now]>dep[x]){if(fa[top[now]]==x)return top[now];now=fa[top[now]];}
return hson[x];
}
inline void modify_path(int x,int y,ll z)
{
int fax=top[x],fay=top[y];
while(fax!=fay){
if(dep[fax]<dep[fay]){
swap(fax,fay);swap(x,y);}
T.update(,n,,num[fax],num[x],z);
x=fa[fax];fax=top[x];}
if(dep[x]>dep[y])swap(x,y);
T.update(,n,,num[x],num[y],z);
}
inline int modify_tree(int x,ll y)
{
int ka=check(x);
switch (ka){
case -:T.update(,n,,,n,y);break;
case :T.update(,n,,num[x],num[x]+size[x]-,y);break;
default:T.update(,n,,,n,y);T.update(,n,,num[ka],num[ka]+size[ka]-,-y);break;
}
}
inline ll quary_path(int x,int y)
{
int fax=top[x],fay=top[y];ll ret=;
while(fax!=fay){
if(dep[fax]<dep[fay]){
swap(fax,fay);swap(x,y);}
ret+=T.quary(,n,,num[fax],num[x]);
x=fa[fax];fax=top[x];}
if(dep[x]>dep[y])swap(x,y);
ret+=T.quary(,n,,num[x],num[y]);
return ret;
}
inline ll quary_tree(int x)
{
int ka=check(x);ll ret;
switch (ka){
case -:ret=T.seg[];break;
case :ret=T.quary(,n,,num[x],num[x]+size[x]-);break;
default:ret=T.seg[]-T.quary(,n,,num[ka],num[ka]+size[ka]-);break;
}
return ret;
}
int main()
{
n=read();int x,y,z,opt;root=;
memset(head,-,sizeof(head));
for(int i=;i<=n;i++)a[i]=read();
for(int i=;i<=n;i++){
x=read();add(x,i);add(i,x);}
dep[]=;dfs1();dfs2(,);
T.build(,n,);m=read();
for(int i=;i<=m;i++){
opt=read();
if(opt==){x=read();root=x;}
else if(opt==){
x=read();y=read();z=read();
modify_path(x,y,z);}
else if(opt==){
x=read();y=read();
modify_tree(x,y);}
else if(opt==){
x=read();y=read();
printf("%lld\n",quary_path(x,y));}
else {x=read();
printf("%lld\n",quary_tree(x));}}
return ;
}

LibreOJ #139 树链剖分 [树链剖分,线段树]的更多相关文章

  1. Qtree3题解(树链剖分(伪)+线段树+set)

    外话:最近洛谷加了好多好题啊...原题入口 这题好像是SPOJ的题,挺不错的.看没有题解还是来一篇... 题意: 很明显吧.. 题解: 我的做法十分的暴力:树链剖分(伪)+线段树+\(set\)... ...

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

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

  3. 【bzoj4999】This Problem Is Too Simple! 树链剖分+动态开点线段树

    题目描述 给您一颗树,每个节点有个初始值. 现在支持以下两种操作: 1. C i x(0<=x<2^31) 表示将i节点的值改为x. 2. Q i j x(0<=x<2^31) ...

  4. P3703-[SDOI2017]树点涂色【LCT,线段树】

    正题 题目链接:https://www.luogu.com.cn/problem/P3703 题目大意 \(n\)个点的一棵树开始所有点有不同的颜色,\(m\)次操作 将根节点到\(x\)节点的路径上 ...

  5. Dynamic Rankings(树状数组套权值线段树)

    Dynamic Rankings(树状数组套权值线段树) 给定一个含有n个数的序列a[1],a[2],a[3]--a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a[ ...

  6. 「ZJOI2017」树状数组(二维线段树)

    「ZJOI2017」树状数组(二维线段树) 吉老师的题目真是难想... 代码中求的是 \(\sum_{i=l-1}^{r-1}a_i\),而实际求的是 \(\sum_{i=l}^{r}a_i\),所以 ...

  7. BZOJ4860 BJOI2017 树的难题 点分治、线段树合并

    传送门 只会线段树……关于单调队列的解法可以去看“重建计划”一题. 看到路径长度$\in [L,R]$考虑点分治.可以知道,在当前分治中心向其他点的路径中,始边(也就是分治中心到对应子树的根的那一条边 ...

  8. BZOJ2141排队——树状数组套权值线段树(带修改的主席树)

    题目描述 排排坐,吃果果,生果甜嗦嗦,大家笑呵呵.你一个,我一个,大的分给你,小的留给我,吃完果果唱支歌,大家 乐和和.红星幼儿园的小朋友们排起了长长地队伍,准备吃果果.不过因为小朋友们的身高有所区别 ...

  9. luogu3380/bzoj3196 二逼平衡树 (树状数组套权值线段树)

    带修改区间K大值 这题有很多做法,我的做法是树状数组套权值线段树,修改查询的时候都是按着树状数组的规则找出那log(n)个线段树根,然后一起往下做 时空都是$O(nlog^2n)$的(如果离散化了的话 ...

  10. poj2528线段树解题报告,离散化+线段树

    题目网址:http://poj.org/problem?id=2528 题意: n(n<=10000)个人依次贴海报,给出每张海报所贴的范围li,ri(1<=li<=ri<=1 ...

随机推荐

  1. 快速排序Quick sort

    快速排序Quick sort 原理,通过一趟扫描将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归 ...

  2. Large Class--过大的类--要重构的信号

    如果想利用单个类做太多事情,其内往往就会出现太多实例变量.一旦如此,Duplicated Code也就接踵而至.     解决方法:     1.将类内彼此相关的变量,将它们放在一起.使用Extrac ...

  3. [BZOJ2754] [SCOI2012]喵星球上的点名解题报告|后缀数组

    a180285幸运地被选做了地球到喵星球的留学生.他发现喵星人在上课前的点名现象非常有趣.   假设课堂上有N个喵星人,每个喵星人的名字由姓和名构成.喵星球上的老师会选择M个串来点名,每次读出一个串的 ...

  4. el-date-picker 日期格式化 yyyy-MM-dd

    <el-date-picker format="yyyy-MM-dd" v-model="dateValue" type="date" ...

  5. OnLoad & DOMReady

    window.onload 事件会在页面或图像加载完成后立即触发(即所有元素的资源都下载完毕).如果页面上有许多图片.音乐或falsh,onload事件会迟迟无法触发.所以出现了DOM Ready事件 ...

  6. python产生随机样本数据

    一.产生X样本 x_train = np.random.random((5, 3)) 随机产生一个5行3列的样本矩阵,也就是5个维度为3的训练样本. array([[ 0.56644011, 0.75 ...

  7. Android 聊天软件客户端

    1.代码架构图 2.qq.model层 3.qq.app层 4.qq.Constatnt层 5.qq.util层 6.qq.broadcast层 7.qq.control层 8.qq.view层 9. ...

  8. 读取BMP图像size的时候与操作和左移的原因

    在读取一个bmp图像的时候,我们会将它的大小读取出来,如果还不清楚bmp的文件结构,那就先看一下这篇博客. 看完我将假设你已经明白所表示的意义.那么,对于bfSize, 它的表示数字为 50 00 0 ...

  9. unbutu下Io language的解释器安装

    今晚看Io,然后要安装解释器,然后就记录下来了... 首先去官网下载 http://iolanguage.com 在页面下方的binaries那里找到自己系统对应的版本,我的是x64deb的,本来是下 ...

  10. tableView选中行的调用顺序/ 取消选中Cell

    UITableViewCell它有两个属性highLighted.selected.很明显一个是高亮状态, 一个是选中状态. UITableViewCell, 对应的2个方法 // 高亮状态调用的方法 ...