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

Posted cytus

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LibreOJ #139 树链剖分 [树链剖分,线段树]相关的知识,希望对你有一定的参考价值。

  题目传送门

树链剖分

题目描述

这是一道模板题。

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

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

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

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

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

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

输入格式

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

第二行 nnn 个整数表示第 iii 个节点的初始权值 aia_ia?i??。

第三行 n−1n-1n1 个整数,表示 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 树链剖分 [树链剖分,线段树]的主要内容,如果未能解决你的问题,请参考以下文章

模板树链剖分

浴谷金秋线上集训营 T11738 伪神(树链剖分)

bzoj 3626 [LNOI2014]LCA(离线处理+树链剖分,线段树)

树链剖分详解

树链剖分 入门

树链剖分