LibreOJ #139 树链剖分 [树链剖分,线段树]
Posted cytus
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LibreOJ #139 树链剖分 [树链剖分,线段树]相关的知识,希望对你有一定的参考价值。
树链剖分
题目描述
这是一道模板题。
给定一棵 nnn 个节点的树,初始时该树的根为 111 号节点,每个节点有一个给定的权值。下面依次进行 mmm 个操作,操作分为如下五种类型:
-
换根:将一个指定的节点设置为树的新根。
-
修改路径权值:给定两个节点,将这两个节点间路径上的所有节点权值(含这两个节点)增加一个给定的值。
-
修改子树权值:给定一个节点,将以该节点为根的子树内的所有节点权值增加一个给定的值。
-
询问路径:询问某条路径上节点的权值和。
-
询问子树:询问某个子树内节点的权值和。
输入格式
第一行为一个整数 nnn,表示节点的个数。
第二行 nnn 个整数表示第 iii 个节点的初始权值 aia_ia?i??。
第三行 n−1n-1n−1 个整数,表示 i+1i+1i+1 号节点的父节点编号 fi+1 (1?fi+1?n)f_{i+1} (1 leqslant f_{i+1} leqslant n)f?i+1?? (1?f?i+1???n)。
第四行一个整数 mmm,表示操作个数。
接下来 mmm 行,每行第一个整数表示操作类型编号:(1?u,v?n)(1 leqslant u, v leqslant n)(1?u,v?n)
-
若类型为 111,则接下来一个整数 uuu,表示新根的编号。
-
若类型为 222,则接下来三个整数 u,v,ku,v,ku,v,k,分别表示路径两端的节点编号以及增加的权值。
-
若类型为 333,则接下来两个整数 u,ku,ku,k,分别表示子树根节点编号以及增加的权值。
-
若类型为 444,则接下来两个整数 u,vu,vu,v,表示路径两端的节点编号。
-
若类型为 555,则接下来一个整数 uuu,表示子树根节点编号。
输出格式
对于每一个类型为 444 或 555 的操作,输出一行一个整数表示答案。
样例
样例输入
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%100\%100% 的数据,1?n,m,k,ai?1051leqslant n,m,k,a_ileqslant 10^51?n,m,k,a?i???10?5??。数据有一定梯度。
分析:
虽然说是树剖模板,但是这个换根操作确实清奇。。。在集训的时候遇到了这题,当时反正是弃疗了。。。实际上换根操作并没有什么影响,在修改查询的时候判断一下就可以了。
Code:
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N=1e5+7; 5 int n,m,root,a[N],head[N],cnt,fa[N],dep[N]; 6 int top[N],dfn[N],num[N],id,size[N],hson[N]; 7 struct Node{int to,next;}edge[N<<1]; 8 struct Seg{ 9 ll seg[N<<4],sign[N<<4]; 10 void pushup(int rt) 11 { 12 seg[rt]=seg[rt<<1]+seg[rt<<1|1]; 13 } 14 void pushdown(int l,int r,int rt) 15 { 16 if(!sign[rt])return; 17 int mid=(l+r)>>1; 18 seg[rt<<1]+=sign[rt]*(mid-l+1); 19 seg[rt<<1|1]+=sign[rt]*(r-mid); 20 sign[rt<<1]+=sign[rt]; 21 sign[rt<<1|1]+=sign[rt]; 22 sign[rt]=0; 23 } 24 void build(int l,int r,int rt) 25 { 26 if(l>r)return; 27 if(l==r){ 28 seg[rt]=a[dfn[l]];return;} 29 int mid=(l+r)>>1; 30 build(l,mid,rt<<1); 31 build(mid+1,r,rt<<1|1); 32 pushup(rt); 33 } 34 void update(int l,int r,int rt,int L,int R,ll C) 35 { 36 if(l>R||r<L)return; 37 if(L<=l&&r<=R){ 38 seg[rt]+=(r-l+1)*C;sign[rt]+=C;return;} 39 int mid=(l+r)>>1; 40 pushdown(l,r,rt); 41 if(L<=mid)update(l,mid,rt<<1,L,R,C); 42 if(R>mid)update(mid+1,r,rt<<1|1,L,R,C); 43 pushup(rt); 44 } 45 ll quary(int l,int r,int rt,int L,int R) 46 { 47 if(l>R||r<L)return 0; 48 if(L<=l&&r<=R){return seg[rt];} 49 int mid=(l+r)>>1;ll ret=0; 50 pushdown(l,r,rt); 51 if(L<=mid)ret+=quary(l,mid,rt<<1,L,R); 52 if(R>mid)ret+=quary(mid+1,r,rt<<1|1,L,R); 53 return ret; 54 } 55 }T; 56 inline int read() 57 { 58 char ch=getchar();int num=0;bool flag=false; 59 while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)flag=true;ch=getchar();} 60 while(ch>=‘0‘&&ch<=‘9‘){num=num*10+ch-‘0‘;ch=getchar();} 61 return flag?-num:num; 62 } 63 inline void add(int x,int y) 64 { 65 edge[++cnt].to=y; 66 edge[cnt].next=head[x]; 67 head[x]=cnt; 68 } 69 inline void dfs1(int u) 70 { 71 size[u]=1; 72 for(int i=head[u];i!=-1;i=edge[i].next){ 73 int v=edge[i].to;if(v==fa[u])continue; 74 fa[v]=u;dep[v]=dep[u]+1; 75 dfs1(v);size[u]+=size[v]; 76 if(size[v]>size[hson[u]])hson[u]=v; 77 } 78 } 79 inline void dfs2(int u,int now) 80 { 81 top[u]=now;num[u]=++id;dfn[id]=u; 82 if(!hson[u])return;dfs2(hson[u],now); 83 for(int i=head[u];i!=-1;i=edge[i].next){ 84 int v=edge[i].to; 85 if(v==fa[u]||v==hson[u])continue; 86 dfs2(v,v);} 87 } 88 inline int check(int x) 89 { 90 if(x==root)return -1; 91 if(!(num[x]<num[root]&&num[x]+size[x]-1>=num[root]))return 0; 92 int now=root; 93 while(dep[now]>dep[x]){if(fa[top[now]]==x)return top[now];now=fa[top[now]];} 94 return hson[x]; 95 } 96 inline void modify_path(int x,int y,ll z) 97 { 98 int fax=top[x],fay=top[y]; 99 while(fax!=fay){ 100 if(dep[fax]<dep[fay]){ 101 swap(fax,fay);swap(x,y);} 102 T.update(1,n,1,num[fax],num[x],z); 103 x=fa[fax];fax=top[x];} 104 if(dep[x]>dep[y])swap(x,y); 105 T.update(1,n,1,num[x],num[y],z); 106 } 107 inline int modify_tree(int x,ll y) 108 { 109 int ka=check(x); 110 switch (ka){ 111 case -1:T.update(1,n,1,1,n,y);break; 112 case 0:T.update(1,n,1,num[x],num[x]+size[x]-1,y);break; 113 default:T.update(1,n,1,1,n,y);T.update(1,n,1,num[ka],num[ka]+size[ka]-1,-y);break; 114 } 115 } 116 inline ll quary_path(int x,int y) 117 { 118 int fax=top[x],fay=top[y];ll ret=0; 119 while(fax!=fay){ 120 if(dep[fax]<dep[fay]){ 121 swap(fax,fay);swap(x,y);} 122 ret+=T.quary(1,n,1,num[fax],num[x]); 123 x=fa[fax];fax=top[x];} 124 if(dep[x]>dep[y])swap(x,y); 125 ret+=T.quary(1,n,1,num[x],num[y]); 126 return ret; 127 } 128 inline ll quary_tree(int x) 129 { 130 int ka=check(x);ll ret; 131 switch (ka){ 132 case -1:ret=T.seg[1];break; 133 case 0:ret=T.quary(1,n,1,num[x],num[x]+size[x]-1);break; 134 default:ret=T.seg[1]-T.quary(1,n,1,num[ka],num[ka]+size[ka]-1);break; 135 } 136 return ret; 137 } 138 int main() 139 { 140 n=read();int x,y,z,opt;root=1; 141 memset(head,-1,sizeof(head)); 142 for(int i=1;i<=n;i++)a[i]=read(); 143 for(int i=2;i<=n;i++){ 144 x=read();add(x,i);add(i,x);} 145 dep[1]=1;dfs1(1);dfs2(1,1); 146 T.build(1,n,1);m=read(); 147 for(int i=1;i<=m;i++){ 148 opt=read(); 149 if(opt==1){x=read();root=x;} 150 else if(opt==2){ 151 x=read();y=read();z=read(); 152 modify_path(x,y,z);} 153 else if(opt==3){ 154 x=read();y=read(); 155 modify_tree(x,y);} 156 else if(opt==4){ 157 x=read();y=read(); 158 printf("%lld ",quary_path(x,y));} 159 else {x=read(); 160 printf("%lld ",quary_tree(x));}} 161 return 0; 162 }
以上是关于LibreOJ #139 树链剖分 [树链剖分,线段树]的主要内容,如果未能解决你的问题,请参考以下文章