题目链接
对这种方法更加详细的介绍与时间复杂度分析: cnblog 洛谷blog
这里介绍一种能够保证时间复杂度的魔改 \(LCT\) 的做法,并非 \(AAAT\)
回想 \(LCT\) 的局限性就是不能维护子树标记
那就可以想到用一个数据结构专门维护每个点的虚儿子
平衡树时空复杂度都表现优秀,那就可以想到直接用一个 \(splay\) 来维护虚儿子
但事实上如果单纯地让每个节点对应一个虚拟节点在对应的维护虚儿子的 \(splay\) 中,
像 \(AAAT\) 一样地写,并不能很好地保证单次 \(access\) 时间复杂度均摊为 \(O(log n)\)
这主要是因为它删除节点时需将前驱(后继)找出来再删
借鉴一下 \(Tarjan\) 的 \(Top\) \(Tree\) 论文,不难发现另一种方法
就是把虚儿子 \(splay\) 中的真正代表原先 \(LCT\) 中信息的点只让它们在叶子结点上
然后用一些辅助节点串起来,构成一个类似 \(leafytree\) 的结构
若在删一个节点时,由于其没有左右儿子,直接连带他的父节点从整个 \(splay\) 中移除,
再将其原先父节点的父节点旋到根,更新一下即可。
同时若是换节点的信息,同样直接改然后旋到根更新信息,十分方便。
而这样是可以保证单次 \(access\) 是均摊时间为一个 \(log\) 的,具体证明参见上面的链接。
而这一题中:换根、求 \(LCA\) 、子树加、子树查询正是这种魔改 \(LCT\) 适用的。
在虚儿子的 \(splay\) 中下传的标记时要对原先 \(LCT\) 的节点信息做修改,
原先 \(LCT\) 的子树标记同样要对其虚儿子的 \(splay\) 打上标记,
为了避免一次这样连续打标记不停,可以在每个点的虚 \(splay\) 中加一个不动的根节点。
但不过虽然时间复杂度能保证,常数还是比 \(Top\) \(Tree\) 要稍微大些。
代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+10;
int n,m,x,y,opt,root=1;ll v,ans;
int son[N][2],anc[N],sz[N],rt[N],rev[N];ll sum[N],w[N],tag[N];
void tag_(int x,ll v);
struct leafy_Splay{
	int son[N<<2][2],anc[N<<2],sz[N<<2],wsz[N<<2],tot;ll sum[N<<2],wsum[N<<2],tag[N<<2];
	queue<int> q;
	void tag_1(int x,ll v){
		sum[x]+=sz[x]*v;
		wsum[x]+=wsz[x]*v;
		tag[x]+=v;
		if(x<=n)tag_(x,v);
	}
	void fix(int x){if(!x)return;sum[x]=sum[son[x][0]]+sum[son[x][1]]+wsum[x];
		sz[x]=sz[son[x][0]]+sz[son[x][1]]+wsz[x];}
	bool p(int x){return son[anc[x]][1]==x;}
	int newnode(){if(!q.empty()){int x=q.front();return q.pop(),x;}return ++tot;}
	void pushdown(int x){
		if(tag[x]){
			if(son[x][0])tag_1(son[x][0],tag[x]);
			if(son[x][1])tag_1(son[x][1],tag[x]);
			tag[x]=0;
		}
	}
	void pushall(int x){
		if(anc[x])pushall(anc[x]);
		pushdown(x);
	}
	void rotate(int x){int y=anc[x],xx=anc[y];bool b=p(x),bb=p(y);
		anc[x]=xx;if(xx)son[xx][bb]=x;son[y][b]=son[x][b^1];
		anc[son[x][b^1]]=y;son[x][b^1]=y;anc[y]=x;fix(y);fix(x);fix(xx);
	}
	void splay(int x,int y){pushall(x);
		for(int i=anc[x];i=anc[x],i!=y;rotate(x))
			if(anc[i]!=y){if(p(x)==p(i))rotate(i);else rotate(x);}
	}
	void split(int x,int y){
		int a=0;
		if(anc[x]!=rt[y])splay(a=anc[x],rt[y]);
		if(anc[x]!=a&&anc[x]!=rt[y])splay(anc[x],a);
		pushdown(anc[x]);pushdown(x);
	}
	void ins(int x,int y,ll sum_,int sz_){
		wsum[x]=sum_;wsz[x]=sz_;fix(x);int tmp=rt[y],f=0;pushdown(tmp);
		if(!son[rt[y]][0])return anc[son[rt[y]][0]=x]=rt[y],fix(rt[y]);
		while(1){
			if(!tmp){
				tmp=newnode();anc[son[anc[f]][p(f)]=tmp]=anc[f];
				son[tmp][x>f]=x;anc[x]=tmp;
				son[tmp][x<f]=f;anc[f]=tmp;
				fix(f);fix(tmp);fix(anc[tmp]);
				return splay(tmp,rt[y]);
			}
			pushdown(tmp);
			f=tmp;tmp=son[tmp][x>tmp];
		}
	}
	void clear(int x){wsz[x]=sz[x]=wsum[x]=sum[x]=son[x][0]=son[x][1]=anc[x]=0;if(x>(n<<1))q.push(x);}
	void del(int x,int y){
		if(anc[x]==rt[y])return son[rt[y]][0]=0,fix(rt[y]),clear(x);
		int yy=anc[x],xx=anc[yy];bool b=p(x),bb=p(yy);
		if(xx==rt[y]){
			anc[son[rt[y]][0]=son[yy][b^1]]=rt[y];
			clear(x),clear(yy),fix(rt[y]);
			return;
		}
		anc[son[xx][p(yy)]=son[yy][b^1]]=xx;
		clear(x);clear(yy);fix(xx);splay(xx,rt[y]);fix(rt[y]);
	}
	void display(int x,int xx,ll sum_,int sz_,int y){
		wsum[xx]=sum_;wsz[xx]=sz_;fix(xx);int yy=anc[x];
		anc[son[yy][p(x)]=xx]=yy;clear(x);fix(yy);if(yy!=rt[y])splay(yy,rt[y]);fix(rt[y]);
	}
}S;
void rev_(int x){rev[x]^=1;son[x][0]^=son[x][1]^=son[x][0]^=son[x][1];}
void tag_(int x,ll v){sum[x]+=v*sz[x];w[x]+=v;tag[x]+=v;S.tag_1(rt[x],v);}
bool p(int x){return son[anc[x]][1]==x;}
bool isroot(int x){return son[anc[x]][0]!=x&&son[anc[x]][1]!=x;}
void fix(int x){
	if(!x)return;
	sum[x]=sum[son[x][0]]+sum[son[x][1]]+w[x]+S.sum[rt[x]];
	sz[x]=sz[son[x][0]]+sz[son[x][1]]+1+S.sz[rt[x]];
}
void pushdown(int x){
	if(rev[x]){if(son[x][0])rev_(son[x][0]);
	if(son[x][1])rev_(son[x][1]);rev[x]=0;}
	if(tag[x]){if(son[x][0])tag_(son[x][0],tag[x]);
	if(son[x][1])tag_(son[x][1],tag[x]);tag[x]=0;}
}
void pushall(int x){
	if(!isroot(x))pushall(anc[x]);
	pushdown(x);
}
void pushall_(int x){
	if(!isroot(x))pushall_(anc[x]);
	else if(anc[x]){
		pushall_(anc[x]);
		S.pushall(x);
	}
	pushdown(x);
}
void rotate(int x){int y=anc[x],xx=anc[y];bool b=p(x),bb=p(y);
	anc[x]=xx;if(!isroot(y))son[xx][bb]=x;son[y][b]=son[x][b^1];
	anc[son[x][b^1]]=y;son[x][b^1]=y;anc[y]=x;fix(y);fix(x);fix(xx);
}
int findrt(int x){if(isroot(x))return x;return findrt(anc[x]);}
void splay(int x){pushall(x);int y=findrt(x);
	if(anc[y]&&x!=y)S.display(y,x,sum[y],sz[y],anc[y]);
	for(int i=anc[x];i=anc[x],!isroot(x);rotate(x)){
		if(!isroot(i)){if(p(i)==p(x))rotate(i);else rotate(x);}
	}
}
int access(int x){
	int y=0;pushall_(x);
	for(;x;y=x,x=anc[x]){
		splay(x);
		if(y&&son[x][1])S.display(y,son[x][1],sum[son[x][1]],sz[son[x][1]],x);
		else if(!y&&son[x][1])S.ins(son[x][1],x,sum[son[x][1]],sz[son[x][1]]);
		else if(y&&!son[x][1])S.del(y,x);
		son[x][1]=y;fix(x);
	}
	return y;
}
void makeroot(int x){access(x);splay(x),rev_(x);}
void split(int x,int y){makeroot(x),access(y),splay(y);}
void link(int x,int y){split(x,y);anc[x]=y;S.ins(x,y,sum[x],sz[x]);fix(y);}
void subtree_add(int x,ll v){
	split(root,x);
	sum[x]+=v*(S.sz[rt[x]]+1);w[x]+=v;
	S.tag_1(rt[x],v);
}
int lca(int x,int y){
	makeroot(root);access(x);return access(y);
}
main(){
	scanf("%d%d",&n,&m);S.tot=n<<1;
	for(int i=1;i<=n;i++)scanf("%lld",&w[i]),fix(i),rt[i]=i+n,S.fix(i);
	for(int i=1;i<n;i++)scanf("%d%d",&x,&y),link(x,y);
	while(m--){
		scanf("%d%d",&opt,&x);
		if(opt==1)root=x;
		if(opt==2)scanf("%d%lld",&y,&v),subtree_add(lca(x,y),v);
		if(opt==3){
			split(root,x);
			ans=sum[son[x][1]]+S.sum[rt[x]]+w[x];
			printf("%lld\n",ans);
		}
	}
}