树的操作(换根树剖)

本文描述了一道数据结构题目,涉及一棵有根树的节点权值和操作,包括查询子树权值和、节点权值加法以及改变根节点。对于新增的换根操作,通过分类讨论并使用线段树与手工栈解决复杂度问题。
摘要由CSDN通过智能技术生成

描述
XXX和 YYY在愉快地刷题。有一道题是这样的:给你一棵 n 个节点的有根树,每个节点有 一个权植。你要支持两种操作:查询以某棵树为根的子树的权值和,给以某个节点为根的整 棵子树的所有点的权值都加上一个值。机智的 XXX 很开心地用LLL教授 讲过的某些东西水水水水过了这道题。

但是可怕的出题人又增加了一种操作:将根节点改为第 u 号节点。于是XXX和YYY 就不会 做了。按照一惯的逻辑,这个问题被交给了你。

注意:初始时,根节点为 1 号节点,所有节点的权值都为 0。

输入
第一行有三个正整数 n, m,表示节点数和操作数。

接下来 n – 1 行每行两个正整数 p, q,表示 p 和 q 节点间有一条边。

接下来 m 行,每行首先有一个正整数 type,描述操作类型。

如果 type=1,接下来有一个正整数 u,表示询问以 u 为根的子树的权值和。

如果 type=2,接下来有两个正整数 u 和 v,表示把以 u 为根的子树的所有节点的权值都加上 v。

如果 type=3,接下来有一个正整数 u,表示把根节点改为第 u 号节点。

输出
对于每一个 1 号操作,输出一行一个非负整数表示答案。

**样例输入 **
3 4
1 2
2 3
2 2 2
1 3
3 3
1 3
样例输出
2
4
提示
【Hint】

对于 30%的数据,1 ≤ n, m ≤ 1,000。
另有 50%的数据不含有 3 号操作。
对于 100%的数据,1 ≤ n, m ≤ 200,000。
对于 100%的数据,1 ≤ u ≤ n, 1 ≤ v ≤ 500,000, 1 ≤ k ≤ n。

如果没有操作 3 3 3就是一道普通的树链剖分,但加入了换根的操作,怎么办?

我们需要考虑换根对维护子树和信息的影响,那么果断分类讨论一波:

我们设当前询问的子树的根为 u u u,当前的根节点为 r o o t root root

第一种情况: l c a lca lca( u u u, r o o t root root)!= u u u,那么我们发现在以 r o o t root root为根时 u u u原来的子树就是现在的子树,直接线段树区间查询。

第二种情况: u u u== r o o t root root,那么此时 u u u就是根节点,直接返回线段树根节点的 m a x n maxn maxn

第三种情况: r o o t root root u u u原来的子树内,即 l c a lca lca( r o o t root root, u u u)== u u u&& u u u!= r o o t root root,怎么做呢?
我们找到 u u u

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
#include <cstdio> #include <iostream> #include <vector> #define N 30003 #define INF 2147483647 using namespace std; int n,f[N][20],dep[N],siz[N],son[N],top[N],tot,pos[N],w[N]; int Max[N*4],Sum[N*4]; vector <int> to[N]; void dfs1(int x){ siz[x]=1; int sz=to[x].size(); for(int i=0;i<sz;++i){ int y=to[x][i]; if(y==f[x][0])continue; f[y][0]=x; dep[y]=dep[x]+1; dfs1(y); siz[x]+=siz[y]; if(siz[y]>siz[son[x]])son[x]=y; } } void dfs2(int x,int root){ top[x]=root; pos[x]=++tot; if(son[x])dfs2(son[x],root); int sz=to[x].size(); for(int i=0;i<sz;++i){ int y=to[x][i]; if(y==f[x][0] || y==son[x])continue; dfs2(y,y); } } void update(int k,int l,int r,int P,int V){ if(l==r){ Max[k]=Sum[k]=V; return; } int mid=(l+r)>>1; if(P<=mid)update(k*2,l,mid,P,V); else update(k*2+1,mid+1,r,P,V); Max[k]=max(Max[k*2],Max[k*2+1]); Sum[k]=Sum[k*2]+Sum[k*2+1]; } void up(int &x,int goal){ for(int i=15;i>=0;--i) if(dep[f[x][i]]>=goal)x=f[x][i]; } int lca(int x,int y){ if(dep[x]>dep[y])up(x,dep[y]); if(dep[x]<dep[y])up(y,dep[x]); if(x==y)return x; for(int i=15;i>=0;--i) if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i]; return f[x][0]; } int getm(int k,int l,int r,int L,int R){ if(L<=l && r<=R)return Max[k]; int res=-INF,mid=(l+r)>>1; if(L<=mid)res=max(res,getm(k*2,l,mid,L,R)); if(R>mid)res=max(res,getm(k*2+1,mid+1,r,L,R)); return res; } int gets(int k,int l,int r,int L,int R){ if(L<=l && r<=R)return Sum[k]; int res=0,mid=(l+r)>>1; if(L<=mid)res+=gets(k*2,l,mid,L,R); if(R>mid)res+=gets(k*2+1,mid+1,r,L,R); return res; } int main(){ scanf("%d",&n); for(int i=1,a,b;i<n;++i){ scanf("%d%d",&a,&b); to[a].push_back(b); to[b].push_back(a); } dep[1]=1; dfs1(1); dfs2(1,1); for(int i=1;i<=15;++i) for(int j=1;j<=n;++j)f[j][i]=f[f[j][i-1]][i-1]; for(int i=1;i<=n;++i){ scanf("%d",&w[i]); update(1,1,n,pos[i],w[i]); } int q; scanf("%d",&q); while(q--){ char s[10]; int u,v,t; scanf("%s",s); if(s[1]=='H'){ scanf("%d%d",&u,&t); w[u]=t; update(1,1,n,pos[u],t); } if(s[1]=='M'){ scanf("%d%d",&u,&v); int ans=-INF,t=lca(u,v); for(int i=u;i;i=f[top[i]][0]) if(dep[t]<dep[top[i]]) ans=max(ans,getm(1,1,n,pos[top[i]],pos[i])); else{ ans=max(ans,getm(1,1,n,pos[t],pos[i])); break; } for(int i=v;i;i=f[top[i]][0]) if(dep[t]<dep[top[i]]) ans=max(ans,getm(1,1,n,pos[top[i]],pos[i])); else{ ans=max(ans,getm(1,1,n,pos[t],pos[i])); break; } printf("%d\n",ans); } if(s[1]=='S'){ scanf("%d%d",&u,&v); int ans=0,t=lca(u,v); for(int i=u;i;i=f[top[i]][0]) if(dep[t]<dep[top[i]]) ans+=gets(1,1,n,pos[top[i]],pos[i]); else{ ans+=gets(1,1,n,pos[t],pos[i]); break; } for(int i=v;i;i=f[top[i]][0]) if(dep[t]<dep[top[i]]) ans+=gets(1,1,n,pos[top[i]],pos[i]); else{ ans+=gets(1,1,n,pos[t],pos[i]); break; } printf("%d\n",ans-w[t]); } } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值