树的操作
描述
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。
Analysis
分析……没有……(我之前写过啦,今日康复训练)
代码……还是有的
Code
#include<bits/stdc++.h>
#define in read()
#define ll long long
#define N 400009
#define M 400009
#define lc (k<<1)
#define rc (k<<1)|1
using namespace std;
inline int read(){
char ch;int f=1,res=0;
while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
while(ch>='0'&&ch<='9'){
res=(res<<3)+(res<<1)+ch-'0';
ch=getchar();
}
return f==1?res:-res;
}
int n,m,tp,rt=1;
int top[N],sze[N],fa[N],dep[N],son[N];
int idx[N],pos[N],cnt=0;
int nxt[M],to[M],head[N],ecnt=0;
struct node{ll lzy,sum;}seg[N<<2];
node operator + (const node &a,const node &b){
node res;res.lzy=0;
res.sum=a.sum+b.sum;
return res;
}
inline void add(int x,int y){
nxt[++ecnt]=head[x];head[x]=ecnt;to[ecnt]=y;
}
void dfs1(int u,int fu){
fa[u]=fu;dep[u]=dep[fu]+1;sze[u]=1;
for(int e=head[u];e;e=nxt[e]){
int v=to[e];
if(v==fu) continue;
dfs1(v,u);
sze[u]+=sze[v];
if(sze[v]>sze[son[u]]) son[u]=v;
}
}
void dfs2(int u){
if(son[u]){
int v=son[u];
idx[pos[v]=++cnt]=v;
top[v]=top[u];
dfs2(v);
}
for(int e=head[u];e;e=nxt[e]){
int v=to[e];
if(v==fa[u]||v==son[u]) continue;
top[v]=v;
idx[pos[v]=++cnt]=v;
dfs2(v);
}
}
inline void pushdown(int k,int l,int r){
int mid=l+r>>1;
seg[lc].lzy+=seg[k].lzy;seg[lc].sum+=(mid-l+1)*1ll*seg[k].lzy;
seg[rc].lzy+=seg[k].lzy;seg[rc].sum+=(r-mid)*1ll*seg[k].lzy;
seg[k].lzy=0;
}
inline node querytree(int k,int l,int r,int x,int y){
if(x<=l&&r<=y) return seg[k];
if(seg[k].lzy) pushdown(k,l,r);
int mid=l+r>>1;
if(y<=mid) return querytree(lc,l,mid,x,y);
if(x>mid) return querytree(rc,mid+1,r,x,y);
return querytree(lc,l,mid,x,y)+querytree(rc,mid+1,r,x,y);
}
inline int LCA(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
x=fa[top[x]];
}
return dep[x]<dep[y]?x:y;
}
inline int find(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
if(fa[top[x]]==y) return top[x];
x=fa[top[x]];
}
return dep[x]<dep[y]?son[x]:son[y];
}
inline ll query(int x){
if(x==rt) return querytree(1,1,n,1,n).sum;//注意特判相等的情况
int lca=LCA(x,rt);
if(lca!=x) return querytree(1,1,n,pos[x],pos[x]+sze[x]-1).sum;
else{
int son=find(x,rt);
ll ans=querytree(1,1,n,1,n).sum;
ans-=querytree(1,1,n,pos[son],pos[son]+sze[son]-1).sum;
return ans;
}
}
inline void modifytree(int k,int l,int r,int x,int y,ll v){
if(x<=l&&r<=y) {
seg[k].lzy+=v;
seg[k].sum+=(r-l+1)*1ll*v;
return;
}
if(seg[k].lzy) pushdown(k,l,r);
int mid=l+r>>1;
if(x<=mid) modifytree(lc,l,mid,x,y,v);
if(y>mid) modifytree(rc,mid+1,r,x,y,v);
seg[k]=seg[lc]+seg[rc];
}
inline void modify(int x,ll v){
int lca=LCA(x,rt);
if(x==rt) return modifytree(1,1,n,1,n,v);
if(lca!=x) modifytree(1,1,n,pos[x],pos[x]+sze[x]-1,v);
else{
int son=find(x,rt);
modifytree(1,1,n,1,n,v);
modifytree(1,1,n,pos[son],pos[son]+sze[son]-1,-v);
}
}
int main(){
n=in;m=in;
int i,j,k,x,y;
for(i=1;i<n;++i){
x=in;y=in;
add(x,y);add(y,x);
}
dfs1(1,0);//处理fa,son,sze,dep
// for(i=1;i<=n;++i) printf("fa=%d son=%d sze=%d dep=%d\n",fa[i],son[i],sze[i],dep[i]);
top[1]=1;idx[1]=++cnt;pos[1]=1;//不要忘了处理 1 ,因为后面dfs2的时候只处理了根的儿子
dfs2(1);//处理top,pos,idx
for(i=1;i<=m;++i){
tp=in;int u,v;u=in;
if(tp==1){
printf("%lld\n",query(u));
}
else if(tp==2){
v=in;
modify(u,v);
}
else rt=u;
}
return 0;
}