题目
题目大意
给定一棵树,每个点都有一个权值,
现在有三种操作,换根,给一个子树里面的所有点加一个值,查询子树内的权值和。
题解
先看没有换根操作,这个就很简单了,
dfs序加上线段树,轻松解决。
再来看看多了换根的操作,
换了根之后,它的子树与在没有换根之前,也就是1为根有什么区别,
分类讨论一下,
假设这个节点x是属于root的子树,那么它的子树根原来的是一模一样的,
如果这个节点在root到根的路径上面,它的子树就变为正课树减去路径上面它的下一个点。
剩下的情况,子树还是没有变。
这样看来,无论换根与否,都只是涉及到整棵子树的,所以也只会用到线段树。
现在就是要考虑在换根之后,如何求lca了。
手玩一下就可以得出结论,
如果要求x,y,在root下的lca,那么就求x,y的,x,root的,y,root的,也就是他们两两之间的,在以1为根的lca,然后在这三个点之中,选出深度最大的,那么这个就在root下的lca。
code
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#define ll long long
#define ls (x<<1)
#define rs (x<<1|1)
#define N 500003
#define P putchar
#define G getchar
using namespace std;
char ch;
void read(int &n)
{
n=0;
ch=G();
while((ch<'0' || ch>'9') && ch!='-')ch=G();
ll w=1;
if(ch=='-')w=-1,ch=G();
while('0'<=ch && ch<='9')n=(n<<3)+(n<<1)+ch-'0',ch=G();
n*=w;
}
int n,m,x,y,z,cz,root,_2[20];
int nxt[N*2],to[N*2],lst[N],fa[N],dep[N],tot,w[N];
ll v[N],si,s[N*4],lazy[N*4],ops,ans;
int id[N],sz[N],now,opl,opr,opx,rank[N],f[19][N],t;
void work(int x,int l,int r)
{
if(opl<=l && r<=opr)
{
if(opx==1)s[x]+=ops*(r-l+1),lazy[x]+=ops;
else ops+=s[x];
return;
}
int m=(l+r)>>1;
if(lazy[x])
{
lazy[ls]+=lazy[x];
lazy[rs]+=lazy[x];
s[ls]+=lazy[x]*(m-l+1);
s[rs]+=lazy[x]*(r-m);
lazy[x]=0;
}
if(opl<=m)work(ls,l,m);
if(m<opr)work(rs,m+1,r);
s[x]=s[ls]+s[rs];
}
void pre(int x,int l,int r)
{
if(l==r)
{
s[x]=w[rank[l]];
return;
}
int m=(l+r)>>1;
pre(ls,l,m);
pre(rs,m+1,r);
s[x]=s[ls]+s[rs];
}
void dfs(int x)
{
id[x]=++now;
dep[x]=dep[fa[x]]+1;
v[x]=w[x];
for(int i=lst[x];i;i=nxt[i])
if(to[i]!=fa[x])
{
fa[to[i]]=x;
dfs(to[i]);
v[x]+=v[to[i]];
}
sz[x]=now;
}
void ins(int x,int y)
{
nxt[++tot]=lst[x];
to[tot]=y;
lst[x]=tot;
}
void dfs(int x,int y)
{
v[x]=w[x]=w[x]+y;si++;
for(int i=lst[x];i;i=nxt[i])
if(to[i]!=fa[x])
{
dfs(to[i],y);
v[x]+=v[to[i]];
}
}
int lca(int x,int y)
{
if(dep[x]<dep[y])swap(x,y);
for(int j=18;j>=0;j--)
if(dep[f[j][x]]>=dep[y])x=f[j][x];
if(x==y)return x;
for(int j=18;j>=0;j--)
if(f[j][x]!=f[j][y])x=f[j][x],y=f[j][y];
return f[0][x];
}
int up(int x,int y)
{
for(int j=18;j>=0;j--)
if(y>=_2[j])y=y-_2[j],x=f[j][x];
return x;
}
int main()
{
_2[0]=1;
for(int i=1;i<20;i++)
_2[i]=_2[i-1]<<1;
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
read(n);read(m);
for(int i=1;i<=n;i++)
read(w[i]);
for(int i=1;i<n;i++)
read(x),read(y),ins(x,y),ins(y,x);
dfs(1);
for(int i=1;i<=n;i++)
rank[id[i]]=i;
pre(1,1,n);
for(int i=1;i<=n;i++)
f[0][i]=fa[i];
for(int j=1;j<19;j++)
for(int i=1;i<=n;i++)
f[j][i]=f[j-1][f[j-1][i]];
for(int i=1;i<=m;i++)
{
read(cz);read(x);
if(cz==1)root=x;else
if(cz==2)
{
read(y);read(z);
t=lca(x,y);
now=lca(x,root);
if(dep[t]<dep[now])t=now;
now=lca(y,root);
if(dep[t]<dep[now])t=now;
if(t==root)
{
opx=1;ops=z;opl=id[1];opr=sz[1];
work(1,1,n);
}
else
if(lca(t,root)!=t)
{
opx=1;ops=z;opl=id[t];opr=sz[t];
work(1,1,n);
}
else
{
now=up(root,dep[root]-dep[t]-1);
opx=1;ops=z;opl=id[1];opr=sz[1];
work(1,1,n);
opx=1;ops=-z;opl=id[now];opr=sz[now];
work(1,1,n);
}
}
else
{
t=x;
if(t==root)
{
opx=2;ops=0;opl=id[1];opr=sz[1];
work(1,1,n);
ans=ops;
}
else
if(lca(t,root)!=t)
{
opx=2;ops=0;opl=id[t];opr=sz[t];
work(1,1,n);
ans=ops;
}
else
{
now=up(root,dep[root]-dep[t]-1);
opx=2;ops=0;opl=id[1];opr=sz[1];
work(1,1,n);
ans=ops;
opx=2;ops=0;opl=id[now];opr=sz[now];
work(1,1,n);
ans=ans-ops;
}
printf("%lld\n",ans);
}
}
return 0;
}