LibreOJ #139 树链剖分 [树链剖分,线段树]

时间:2022-09-11 15:37:35

  题目传送门

树链剖分

题目描述

这是一道模板题。

给定一棵 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 ;
}