LibreOJ #139 树链剖分 [树链剖分,线段树]
树链剖分
题目描述
这是一道模板题。
给定一棵 n 个节点的树,初始时该树的根为 1 号节点,每个节点有一个给定的权值。下面依次进行 m 个操作,操作分为如下五种类型:
-
换根:将一个指定的节点设置为树的新根。
-
修改路径权值:给定两个节点,将这两个节点间路径上的所有节点权值(含这两个节点)增加一个给定的值。
-
修改子树权值:给定一个节点,将以该节点为根的子树内的所有节点权值增加一个给定的值。
-
询问路径:询问某条路径上节点的权值和。
-
询问子树:询问某个子树内节点的权值和。
输入格式
第一行为一个整数 n,表示节点的个数。
第二行 nn个整数表示第 i 个节点的初始权值 ai。
第三行 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+7; 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<<1]; struct Seg{ ll seg[N<<4],sign[N<<4]; void pushup(int rt) { seg[rt]=seg[rt<<1]+seg[rt<<1|1]; } void pushdown(int l,int r,int rt) { if(!sign[rt])return; int mid=(l+r)>>1; seg[rt<<1]+=sign[rt]*(mid-l+1); seg[rt<<1|1]+=sign[rt]*(r-mid); sign[rt<<1]+=sign[rt]; sign[rt<<1|1]+=sign[rt]; sign[rt]=0; } 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)>>1; build(l,mid,rt<<1); build(mid+1,r,rt<<1|1); 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+1)*C;sign[rt]+=C;return;} int mid=(l+r)>>1; pushdown(l,r,rt); if(L<=mid)update(l,mid,rt<<1,L,R,C); if(R>mid)update(mid+1,r,rt<<1|1,L,R,C); pushup(rt); } ll quary(int l,int r,int rt,int L,int R) { if(l>R||r<L)return 0; if(L<=l&&r<=R){return seg[rt];} int mid=(l+r)>>1;ll ret=0; pushdown(l,r,rt); if(L<=mid)ret+=quary(l,mid,rt<<1,L,R); if(R>mid)ret+=quary(mid+1,r,rt<<1|1,L,R); return ret; } }T; inline int read() { char ch=getchar();int num=0;bool flag=false; while(ch<'0'||ch>'9'){if(ch=='-')flag=true;ch=getchar();} while(ch>='0'&&ch<='9'){num=num*10+ch-'0';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]=1; for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].to;if(v==fa[u])continue; fa[v]=u;dep[v]=dep[u]+1; 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!=-1;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 -1; if(!(num[x]<num[root]&&num[x]+size[x]-1>=num[root]))return 0; 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(1,n,1,num[fax],num[x],z); x=fa[fax];fax=top[x];} if(dep[x]>dep[y])swap(x,y); T.update(1,n,1,num[x],num[y],z); } inline int modify_tree(int x,ll y) { int ka=check(x); switch (ka){ case -1:T.update(1,n,1,1,n,y);break; case 0:T.update(1,n,1,num[x],num[x]+size[x]-1,y);break; default:T.update(1,n,1,1,n,y);T.update(1,n,1,num[ka],num[ka]+size[ka]-1,-y);break; } } inline ll quary_path(int x,int y) { int fax=top[x],fay=top[y];ll ret=0; while(fax!=fay){ if(dep[fax]<dep[fay]){ swap(fax,fay);swap(x,y);} ret+=T.quary(1,n,1,num[fax],num[x]); x=fa[fax];fax=top[x];} if(dep[x]>dep[y])swap(x,y); ret+=T.quary(1,n,1,num[x],num[y]); return ret; } inline ll quary_tree(int x) { int ka=check(x);ll ret; switch (ka){ case -1:ret=T.seg[1];break; case 0:ret=T.quary(1,n,1,num[x],num[x]+size[x]-1);break; default:ret=T.seg[1]-T.quary(1,n,1,num[ka],num[ka]+size[ka]-1);break; } return ret; } int main() { n=read();int x,y,z,opt;root=1; memset(head,-1,sizeof(head)); for(int i=1;i<=n;i++)a[i]=read(); for(int i=2;i<=n;i++){ x=read();add(x,i);add(i,x);} dep[1]=1;dfs1(1);dfs2(1,1); T.build(1,n,1);m=read(); for(int i=1;i<=m;i++){ opt=read(); if(opt==1){x=read();root=x;} else if(opt==2){ x=read();y=read();z=read(); modify_path(x,y,z);} else if(opt==3){ x=read();y=read(); modify_tree(x,y);} else if(opt==4){ x=read();y=read(); printf("%lld\n",quary_path(x,y));} else {x=read(); printf("%lld\n",quary_tree(x));}} return 0; }
如需转载,请署名作者并附上原文链接,蒟蒻非常感激
名称:HolseLee
博客地址:www.cnblogs.com/cytus
个人邮箱:1073133650@qq.com