Link Cut Tree

Link Cut Tree

LCT专题总结

Part 1 LCT 原理介绍

(以下均为个人见解)

LCT是一种树链剖分,奇怪的树链剖分

根据我的认知,LCT是基于将整颗树随机地剖成若干条能够动态改变的链,每条链均是一条到达祖先的路径

整棵LCT就是由这样的链构成,并且这些链可以动态重构和连接

LCT最核心的操作就是\(Access\)

如何理解这个操作呢?

\(Access(x)\)就是构造出一条\(x\)到根的路径构成的链,并且构造完成后链上没有\(x\)的儿子

算法实现起来就是:

切掉\(x\)的儿子

\(while\) \(x\)所在链的顶端不是\(root\)

​ 切掉\(fa[top[x]\)]的儿子

​ 将\(x\)所在的链接在\(fa[top[x]]\)下面

我们亲爱的 \(tarjan\)老先生 证明了这个复杂度是\(log(n)\)

那如何搞定这个断链接链的操作呢?

\(Splay\)来搞定就行了

这样,我们的\(LCT\)算法,就是将原来的树,剖成了若干条链,每条链由一棵\(Splay\)维护,按照深度构树

Part 2 LCT基本操作

说的轻巧,写起来还是贼**

首先我么考虑存储

通常我们使用的\(LCT\)写法都是讲\(Splay\)里的\(fa\)数组和链与链之间的\(fa\)数组放在了一起

也就是说:

如果这个节点是它所在\(Splay\)的根,那么它的\(fa\)就是树上的\(fa\)

否则就是\(Splay\)上的\(fa\)

为了辨别它是不是Splay的跟,我们又引入了一个函数

inline int isroot(int x){ return !fa[x]||(son[fa[x]][1]!=x&&son[fa[x]][0]!=x); }

\(Access\)

现在我们来看\(Access\)操作的代码实现

//while version
void Access(int x){
    int t=0;
	while(x){
        Splay(x);
        son[x][1]=t; 
        // 这里的这个操作不仅是将t接到了x上,同时也能把x的儿子断掉
        x=fa[x];
	}
}

//for version
void Access(int x){ for(int t=0;x;t=x,x=fa[x]) Splay(x),son[x][1]=t; }

tips:如果这道题有\(Up()\)操作,\(Access\)时别忘了

好我们先练几道模板题

[BZOJ 2002] Bounce 弹飞绵羊

就是维护深度,支持动态连边

#include<cstdio>
#include<cctype>
#include<iostream>
#include<cassert>
#include<cmath>
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)


char IO;
int rd(){
	int s=0,f=0;
	while(!isdigit(IO=getchar())) if(IO=='-') f=1;
	do s=(s<<1)+(s<<3)+(IO^'0');
	while(isdigit(IO=getchar()));
	return f?-s:s;
}

const int N=2e5+10;

int n,m;
int fa[N],son[N][2],top[N],sz[N];

inline int dir(int x){ return son[fa[x]][1]==x; }
inline int isroot(int x){ return !fa[x]||(son[fa[x]][1]!=x&&son[fa[x]][0]!=x); }

void Up(int u) {
	if(!u) return;
	sz[u]=sz[son[u][0]]+sz[son[u][1]]+1;
}

void rotate(int u) {
	int f=fa[u],ff=fa[f],d=dir(u);
	fa[u]=ff; if(!isroot(f)) son[ff][dir(f)]=u;
	son[f][d]=son[u][!d]; if(son[u][!d]) fa[son[u][!d]]=f;
	fa[f]=u,son[u][!d]=f;
	Up(f),Up(u),Up(ff);
}
void Splay(int u) {
	if(!u) return;
	Up(u);
	while(!isroot(u)) {
		int f=fa[u];
		if(!isroot(f)) {
			if(dir(u)^dir(f)) rotate(u);
			else rotate(f);
		}
		rotate(u);
	}
}

void Access(int x) {
	int t=0;
	while(x) {
		Splay(x);
		son[x][1]=t;
		Up(x);
		x=fa[x];
	}
}

int Que(int x) {
	Access(x);
	Splay(x);
	return sz[son[x][0]];
}

void Link(int x,int to) {
	Splay(x);
	fa[son[x][0]]=fa[x],son[x][0]=0;
	fa[x]=to;
}

int main(){
	rep(i,1,n=rd()) {
		int x=min(n+1,rd()+i);
		fa[i]=x;
		Up(i);
	}
	Up(n+1);
	rep(i,1,rd()) {
		int opt=rd();
		if(opt==1) printf("%d\n",Que(rd()+1));
		else {
			int x=rd()+1,t=rd();
			Link(x,min(n+1,x+t));
		}
	}
}


\[\ \]

\[\ \]

\(MakeRoot\)操作

即改变\(LCT\)的根

(由于博主能力有限,请读者画图自行理解一下)

void MakeRoot(int x){
    Access(x);
    Splay(x);
    rev[x]^=1;
    swap(son[x][0],son[x][1]);
}

就是构造一条到跟的链,然后将这条链翻转,翻转之后就能够让\(x\)变成根,同时树的其他部分并不需要改变

这个操作就要打标记了

[BZOJ 2049] Cave 洞穴勘测

这道题要多一个\(GetRoot\)操作,这个也不必多讲了吧

#include<cstdio>
#include<cctype>
#include<iostream>
#include<cassert>
#include<cmath>
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)


char IO;
int rd(){
	int s=0,f=0;
	while(!isdigit(IO=getchar())) if(IO=='-') f=1;
	do s=(s<<1)+(s<<3)+(IO^'0');
	while(isdigit(IO=getchar()));
	return f?-s:s;
}

const int N=1e4+10;

int n,m;
int fa[N],son[N][2];
int rev[N];

inline int dir(int x){ return son[fa[x]][1]==x; }
inline int isroot(int x){ return !fa[x] || (son[fa[x]][0]!=x && son[fa[x]][1]!=x); }

void Down(int u) {
	if(!u || !rev[u]) return;
	int t;
	if(son[u][0]) {
		t=son[u][0];
		rev[t]^=1;
		swap(son[t][0],son[t][1]);
	}
	if(son[u][1]) {
		t=son[u][1];
		rev[t]^=1;
		swap(son[t][0],son[t][1]);
	}
	rev[u]=0;
}

void rotate(int u) {
	int f=fa[u],ff=fa[f];
	if(!isroot(f)) Down(ff);
	Down(f),Down(u);
	int d=dir(u);
	fa[u]=ff; if(!isroot(f)) son[ff][dir(f)]=u;
	son[f][d]=son[u][!d]; if(son[u][!d]) fa[son[u][!d]]=f;
	fa[f]=u,son[u][!d]=f;
}
void Splay(int u) {
	Down(u);
	while(!isroot(u)) {
		int f=fa[u];
		if(!isroot(f)) {
			if(dir(u)^dir(f)) rotate(u);
			else rotate(f);
		}
		rotate(u);
	}
}

void Access(int x) {
	int t=0;
	while(x) {
		Splay(x);
		son[x][1]=t,fa[t]=x,t=x;
		x=fa[x];
	}
}

void makeroot(int x) {
	Access(x);
	Splay(x);
	rev[x]^=1;
	swap(son[x][0],son[x][1]);
}

int GetRoot(int x){ 
	Access(x),Splay(x);
	Down(x);
	while(son[x][0]) {
		x=son[x][0];
		Down(x);
	}
	Splay(x);
	return x;
}

int Que(int x,int y) {
	return GetRoot(x)==GetRoot(y);
}

void Link(int x,int y) {
	makeroot(x);
	fa[x]=y;
}

void Cut(int x,int y) {
	makeroot(x);
	Access(y);
	Splay(y);
	fa[x]=0;
	son[y][0]=0;
}


char opt[10];

int main(){
	n=rd(),m=rd();
	rep(i,1,m) {
		scanf("%s",opt);
		if(opt[0]=='Q') puts(Que(rd(),rd())?"Yes":"No");
		else if(opt[0]=='C') Link(rd(),rd());
		else Cut(rd(),rd());
	}
}


\[\ \]

\[\ \]

路径查询

据我所知,路径查询有两种写法

一种是先\(Access(x)\),再把\(y\) \(Access\)\(x\)下面(部分\(Access\)),然后就有几种情况讨论一下

当然可以直接就\(MakeRoot(x),Access(y),Splay(y)\),直接访问,简单粗暴,但是要打标记

[SPOJ QTREE] Query on a tree

树剖线段树裸题。。。

但是\(LCT\)\(log\)!

由于边是静态的,所以就是一个简单的路径查询

这里我唯一一次写了第一种查询

#include<cstdio>
#include<cctype>
#include<iostream>
#include<cassert>
#include<cmath>
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)


char IO;
int rd(){
	int s=0,f=0;
	while(!isdigit(IO=getchar())) if(IO=='-') f=1;
	do s=(s<<1)+(s<<3)+(IO^'0');
	while(isdigit(IO=getchar()));
	return f?-s:s;
}

const int N=1e4+10;

int n,m;
int fa[N],son[N][2],s[N],val[N];
int id[N],num[N];

inline int dir(int x){ return son[fa[x]][1]==x; }
inline int isroot(int x){ return !fa[x]||(son[fa[x]][0]!=x&&son[fa[x]][1]!=x); }


void Up(int u) {
	s[u]=max(val[u],max(s[son[u][0]],s[son[u][1]]));
}

void rotate(int u){
	int f=fa[u],ff=fa[f],d=dir(u);
	int t=isroot(f);
	fa[u]=ff; if(!t) son[ff][dir(f)]=u;
	son[f][d]=son[u][!d]; if(son[u][!d]) fa[son[u][!d]]=f;
	fa[f]=u,son[u][!d]=f;
	Up(f),Up(u);
	if(!t) Up(ff); 
}
void Splay(int u) {
	while(!isroot(u)) {
		int f=fa[u];
		if(!isroot(f)) {
			if(dir(u)^dir(f)) rotate(u);
			else rotate(f);
		}
		rotate(u);
	}
}

int Que(int x,int y) {
	if(x==y) return 0;
	int t1=0,t2=0,tx=x;
	while(x) {
		Splay(x);
		son[x][1]=t1;
		Up(x); t1=x; x=fa[x];
	}
	while(y) {
		Splay(y);
		if(!fa[y]) break;
		son[y][1]=t2;
		Up(y); t2=y; y=fa[y];
	}
	if(!t2) return s[son[y][1]];
	int ans=s[t2];
	if(t2==tx) return ans;
	ans=max(ans,s[son[y][1]]);
	return ans;
}

struct Edge{
	int to,nxt,w,id;
}e[N<<1];
int head[N],ecnt;
void AddEdge(int u,int v,int w,int id) {
	e[++ecnt]=(Edge){v,head[u],w,id};
	head[u]=ecnt;
}
void pre_dfs(int u,int f) {
	son[u][0]=son[u][1]=0;
	for(int i=head[u];i;i=e[i].nxt) {
		int v=e[i].to;
		if(v==f) continue;
		id[e[i].id]=v,fa[v]=u;
		s[v]=val[v]=e[i].w;
		pre_dfs(v,u);
	}
}
char opt[10];
int main(){
	rep(kase,1,rd()) {
		n=rd();
		rep(i,1,n) head[i]=0; ecnt=0;
		rep(i,1,n-1) {
			int u=rd(),v=rd(),w=rd();
			AddEdge(u,v,w,i);
			AddEdge(v,u,w,i);
		}
		fa[1]=0;
		pre_dfs(1,0);
		while(~scanf("%s",opt) && opt[0]!='D') {
			if(opt[0]=='Q') printf("%d\n",Que(rd(),rd()));
			else {
				int x=rd(),y=rd();
				val[id[x]]=y;
				Splay(id[x]);
			}
		}
	}
}


\[\ \]

\[\ \]

HDU - 4010

多了几个操作,\(Debug\)警告!

#include<cstdio>
#include<cctype>
#include<iostream>
#include<cassert>
#include<cmath>
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)




char IO;
int rd(){
	int s=0,f=0;
	while(!isdigit(IO=getchar())) if(IO=='-') f=1;
	do s=(s<<1)+(s<<3)+(IO^'0');
	while(isdigit(IO=getchar()));
	return f?-s:s;
}

const int N=3e5+10;

int n,m;
int fa[N],son[N][2];
int s[N],val[N],t[N];
int rev[N];

struct Edge{
	int to,nxt;
}e[N<<1];
int head[N],ecnt;
void AddEdge(int u,int v){
	e[++ecnt]=(Edge){v,head[u]};
	head[u]=ecnt;
}

inline int dir(int x) { return son[fa[x]][1]==x; }
inline int isroot(int x){ return !fa[x] || (son[fa[x]][0]!=x && son[fa[x]][1]!=x); }

void Down(int u) {
	if(!u) return;
	if(rev[u]) {
		if(son[u][0]) {
			rev[son[u][0]]^=1;
			swap(son[son[u][0]][0],son[son[u][0]][1]);
		}
		if(son[u][1]) {
			rev[son[u][1]]^=1;
			swap(son[son[u][1]][0],son[son[u][1]][1]);
		}
		rev[u]=0;
	}
	if(t[u]) {
		if(son[u][0]) {
			t[son[u][0]]+=t[u];
			s[son[u][0]]+=t[u];
			val[son[u][0]]+=t[u];
		}
		if(son[u][1]) {
			t[son[u][1]]+=t[u];
			s[son[u][1]]+=t[u];
			val[son[u][1]]+=t[u];
		}
		t[u]=0;
	}
}
void Up(int u) {
	if(!u) return;
	s[u]=max(val[u],max(s[son[u][0]],s[son[u][1]]));
}

void rotate(int u) {
	int t=!isroot(fa[u]);
	if(t) Down(fa[fa[u]]);
	Down(fa[u]),Down(u);
	int f=fa[u],ff=fa[f],d=dir(u);
	fa[u]=ff; if(t) son[ff][dir(f)]=u;
	son[f][d]=son[u][!d]; if(son[u][!d]) fa[son[u][!d]]=f;
	fa[f]=u,son[u][!d]=f;
	Up(f),Up(u);
	if(t) Up(ff);
}
void Splay(int u) {
	static int stk[N],top=0;
	int t=u;
	while(1) {
		stk[++top]=t;
		if(isroot(t)) break;
		t=fa[t];
	}
	while(top) Down(stk[top--]);
	Down(u);
	while(!isroot(u)) {
		int f=fa[u];
		if(!isroot(f)) {
			if(dir(u)^dir(f)) rotate(u);
			else rotate(f);
		}
		rotate(u);
	}
}

void Access(int x) {
	int t=0;
	while(x) {
		Splay(x);
		son[x][1]=t;
		Up(x);
		t=x,x=fa[x];
	}
}

void pre_dfs(int u,int f) {
	fa[u]=f;
	son[u][0]=son[u][1]=rev[u]=t[u]=0;
	for(int i=head[u];i;i=e[i].nxt) {
		int v=e[i].to;
		if(v==f) continue;
		pre_dfs(v,u);
	}
}

int GetRoot(int x) {
	Access(x);
	Splay(x);
	while(1) {
		Down(x);
		if(!son[x][0]) break;
		x=son[x][0];
	}
	Splay(x);
	return x;
}
void MakeRoot(int x) {
	Access(x);
	Splay(x);
	rev[x]^=1;
	swap(son[x][0],son[x][1]);
}

void Link(int x,int y) {
	if(GetRoot(x)==GetRoot(y)) return (void)puts("-1");
	MakeRoot(x);
	fa[x]=y;
}
void Cut(int x,int y) {
	swap(x,y);
	if(x==y || GetRoot(x)!=GetRoot(y)) return (void)puts("-1");
	MakeRoot(x);
	Access(y);
	Splay(y);
	fa[son[y][0]]=0;
	son[y][0]=0;
}

void Add(int x,int y,int w) {
	if(GetRoot(x)!=GetRoot(y)) return (void)puts("-1");
	MakeRoot(x);
	Access(y);
	Splay(y); 
	s[y]+=w,t[y]+=w,val[y]+=w;
}

int Que(int x,int y){
	if(GetRoot(x)!=GetRoot(y)) return -1;
	MakeRoot(x);
	Access(y);
	Splay(y);
	return s[y];
}

int main(){
	while(~scanf("%d",&n)) {
		rep(i,1,n) head[i]=0; ecnt=0;
		rep(i,2,n) {
			int u=rd(),v=rd();
			AddEdge(u,v);
			AddEdge(v,u);
		}
		rep(i,1,n) s[i]=val[i]=rd();
		pre_dfs(1,0);
		rep(ttt,1,m=rd()) {
			int opt=rd();
			if(opt==1) Link(rd(),rd());
			if(opt==2) Cut(rd(),rd());
			if(opt==3) {
				int w=rd(),x=rd(),y=rd();
				Add(x,y,w);
			}
			if(opt==4) printf("%d\n",Que(rd(),rd()));
		}
		puts("");
	}
}




\[\ \]

\[\ \]

HDU - 5052

城市贸易动态版

如果要\(MakeRoot\)打翻转标记的话,存一个正的存一个反的直接换就行了

#include<cstdio>
#include<cctype>
#include<iostream>
#include<cassert>
#include<cmath>
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)

char IO;
int rd(){
	int s=0,f=0;
	while(!isdigit(IO=getchar())) if(IO=='-') f=1;
	do s=(s<<1)+(s<<3)+(IO^'0');
	while(isdigit(IO=getchar()));
	return f?-s:s;
}

const int N=5e4+10;

int n,m;
int fa[N],son[N][2];

inline int max(int x,int y) { return x>y?x:y; }
inline int min(int x,int y) { return x<y?x:y; }
inline int dir(int x){ return son[fa[x]][1]==x; }
inline int isroot(int x){ return !fa[x] || (son[fa[x]][1]!=x && son[fa[x]][0]!=x);  }

struct Node{
	int ma,mi,ans,rans;
	Node operator + (const Node x) const {
		Node res;
		res.ma=max(ma,x.ma);
		res.mi=min(mi,x.mi);
		res.ans=max(max(ans,x.ans),ma-x.mi);
		res.rans=max(max(rans,x.rans),x.ma-mi);
		return res;
	}
}s[N],val[N];


int rev[N],t[N];
void Up(int u) {
	if(!u) return;
	s[u]=val[u];
	if(son[u][0]) s[u]=s[son[u][0]]+s[u];
	if(son[u][1]) s[u]=s[u]+s[son[u][1]];
}
void Down(int u) {
	if(!u) return;
	int x;
	if(rev[u]) {
		if(son[u][0]) {
			x=son[u][0];
			rev[x]^=1;
			swap(son[x][0],son[x][1]);
			swap(s[x].ans,s[x].rans);
		}
		if(son[u][1]) {
			x=son[u][1];
			rev[x]^=1;
			swap(son[x][0],son[x][1]);
			swap(s[x].ans,s[x].rans);
		}
		rev[u]=0;
	}
	if(t[u]) {
		if(son[u][0]) {
			x=son[u][0];
			t[x]+=t[u];
			s[x].ma+=t[u],s[x].mi+=t[u],val[x].ma+=t[u],val[x].mi+=t[u];
		}
		if(son[u][1]) {
			x=son[u][1];
			t[x]+=t[u];
			s[x].ma+=t[u],s[x].mi+=t[u],val[x].ma+=t[u],val[x].mi+=t[u];
		}
		t[u]=0;
	}
}

void rotate(int u) {
	int f=fa[u],ff=fa[f];
	Down(ff),Down(f),Down(u);
	int d=dir(u);
	fa[u]=ff; if(!isroot(f)) son[ff][dir(f)]=u; 
	son[f][d]=son[u][!d]; if(son[u][!d]) fa[son[u][!d]]=f;
	fa[f]=u,son[u][!d]=f;
	Up(f),Up(u),Up(ff);
}
void Splay(int x){
	Down(x);
	while(!isroot(x)) {
		int f=fa[x];
		if(!isroot(f)) {
			if(dir(f)^dir(x)) rotate(x);
			else rotate(f);
		}
		rotate(x);
	}
}

void Access(int x) { for(int t=0;x;t=x,x=fa[x])  Splay(x),son[x][1]=t,Up(x); }
void MakeRoot(int x) {
	Access(x);
	Splay(x);
	rev[x]^=1;
	swap(son[x][0],son[x][1]);
	swap(s[x].rans,s[x].ans);
}

int Que(int x,int y,int w) {
	MakeRoot(x);
	Access(y);
	Splay(y);
	int t=s[y].ans;
	::t[y]+=w,s[y].ma+=w,s[y].mi+=w;
	val[y].mi+=w,val[y].ma+=w;
	return t;
}

struct Edge{ int to,nxt; } e[N<<1];
int head[N],ecnt;
void AddEdge(int u,int v) {
	e[++ecnt]=(Edge){v,head[u]};
	head[u]=ecnt;
}
#define erep(u,i) for(int i=head[u];i;i=e[i].nxt) 

void pre_dfs(int u,int f) {
	fa[u]=f;son[u][0]=son[u][1]=t[u]=rev[u]=0;
	erep(u,i) {
		int v=e[i].to;
		if(v==f) continue;
		pre_dfs(v,u);
	}
}

int main(){
	rep(kase,1,rd()) {
		n=rd();
		rep(i,1,n) {
			int x=rd();
			head[i]=0;
			s[i]=val[i]=(Node){x,x,0,0};
		}
		ecnt=0;
		rep(i,2,n) {
			int u=rd(),v=rd();
			AddEdge(u,v);
			AddEdge(v,u);
		}
		pre_dfs(1,0);
		rep(i,1,m=rd()) {
			int y=rd(),x=rd(),w=rd();
			printf("%d\n",Que(x,y,w));
		}
	}
}

HDU - 5398

这题需要我们弄一些小性质

假设我们从大的数向小的数连边,那么一定是向大的数的因子连边

如果这条边已经存在,那么就要从当前产生的环上换下来,存一个最小值

这样就是\(LCT\)模拟连边过程,总复杂度$n \cdot log \ n \cdot ln \ n $

#include<cstdio>
#include<cctype>
#include<iostream>
#include<vector>
#include<cmath>
#include<algorithm>
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)

char IO;
int rd(){
	int s=0,f=0;
	while(!isdigit(IO=getchar())) if(IO=='-') f=1;
	do s=(s<<1)+(s<<3)+(IO^'0');
	while(isdigit(IO=getchar()));
	return f?-s:s;
}

const int N=2e5+10;

int n=1e5;
vector <int> fac[N];
int fa[N],son[N][2];
int rev[N];
struct Node{
	int x,id;
	Node operator + (const Node t) const{
		Node res;
		res.x=min(x,t.x);
		if(res.x==x) res.id=id;
		else res.id=t.id;
		return res;
	}
} s[N],val[N];

inline int dir(int x){ return son[fa[x]][1]==x; }
inline int isroot(int x){ return !fa[x] || (son[fa[x]][0]!=x&&son[fa[x]][1]!=x); }

void Up(int u) {
	if(!u) return;
	s[u]=val[u];
	if(son[u][0]) s[u]=s[u]+s[son[u][0]];
	if(son[u][1]) s[u]=s[u]+s[son[u][1]];
}
void Down(int u) {
	if(!u||!rev[u]) return;
	int t;
	if(son[u][0]) {
		t=son[u][0];
		rev[t]^=1;
		swap(son[t][0],son[t][1]);
	}
	if(son[u][1]) {
		t=son[u][1];
		rev[t]^=1;
		swap(son[t][0],son[t][1]);
	}
	rev[u]=0;
}

int stk[N],top;
int eu[N],ev[N];

void rotate(int u) {
	int f=fa[u],ff=fa[f];
	Down(ff),Down(f),Down(u);
	int d=dir(u);
	fa[u]=ff; if(!isroot(f)) son[ff][dir(f)]=u;
	son[f][d]=son[u][!d]; fa[son[u][!d]]=f;
	fa[f]=u,son[u][!d]=f;
	Up(f),Up(u),Up(ff);
}

void Splay(int x) {
	Down(x),Up(x);
	static int stk[N],top=0;
	int t=x;
	while(!isroot(t)) stk[++top]=t,t=fa[t];
	stk[++top]=t;
	while(top) Down(stk[top--]);
	while(!isroot(x)) {
		int f=fa[x];
		if(!isroot(f)) {
			if(dir(x)^dir(f)) rotate(x);
			else rotate(f);
		}
		rotate(x);
	}
}



void Access(int x){ for(int t=0; x; t=x,x=fa[x]) Splay(x),son[x][1]=t,Up(x); }
void MakeRoot(int x) {
	Access(x);
	Splay(x);
	rev[x]^=1;
	swap(son[x][0],son[x][1]);
}

int GetRoot(int x) { 
	Access(x);
	Splay(x);
	do {
		Down(x);
		if(!son[x][0]) break;
		x=son[x][0];
	} while(1);
	Splay(x);
	return x;
}


void Cut(int x,int y) {
	MakeRoot(x);
	Access(y);
	Splay(x);
	fa[y]=0;
	son[x][1]=0;
	Up(x);
}


ll ans;
ll res[N];



void Link(int x,int y,int w) {
	if(GetRoot(x)==GetRoot(y)) {
		MakeRoot(x);
		Access(y);
		Node t=s[y];
		if(t.x>=w) return;
		ans-=t.x;
		Cut(eu[t.id],t.id);
		Cut(ev[t.id],t.id);
		son[t.id][0]=son[t.id][1]=fa[t.id]=rev[t.id]=0;
		stk[++top]=t.id;
	}
	MakeRoot(y);
	int t=stk[top--];
	fa[t]=rev[t]=0,son[t][0]=son[t][1]=0,s[t]=val[t]=(Node){w,t};
	eu[t]=x,ev[t]=y;
	ans+=w;
	fa[t]=x;
	fa[y]=t;
}



int main(){
	rep(i,n+1,2*n) stk[++top]=i;
	rep(i,1,n) s[i]=val[i]=(Node){(int)1e9,i};
	for(reg int i=1;i<=n;++i) for(reg int j=i+i;j<=n;j+=i) fac[j].push_back((int)i);
	rep(i,2,n) {
		drep(j,fac[i].size()-1,0) Link(i,fac[i][j],fac[i][j]);
		res[i]=ans;
	}
	while(~scanf("%d",&n)) printf("%lld\n",res[n]);
}




HDU - 5333

询问离线,从左到右,每次将\(max(u,v) \leq i\)的边加入,同时按照\(min(u,v)\)较大的顺序将边保留,保证当前图是一棵生成树,并且图上的边是每一个环上最大的(即动态维护最大生成树),用树状数组维护边权\(\geq j\)的总数,即联通块减少的数量

#include<cstdio>
#include<cctype>
#include<iostream>
#include<vector>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)

char IO;
int rd(){
	int s=0,f=0;
	while(!isdigit(IO=getchar())) if(IO=='-') f=1;
	do s=(s<<1)+(s<<3)+(IO^'0');
	while(isdigit(IO=getchar()));
	return f?-s:s;
}
const int N=2e5+10;

int n,m,q;
struct Edge{
	int u,v;
	int operator < (const Edge __) const {
		return max(u,v)<max(__.u,__.v);
	}
}E[N];
struct Node{
	int x,id;
	Node operator + (const Node __) const{
		Node res;
		res.x=min(x,__.x);
		if(res.x==x) res.id=id;
		else res.id=__.id;
		return res;
	}
}s[N],val[N];
struct Query{
	int l,r,id;
	bool operator < (const Query __) const{
		return r<__.r;
	}
}Q[N];
int Ans[N];
int eu[N],ev[N];
int stk[N],top;
int fa[N],son[N][2],rev[N];
inline int dir(int x){ return son[fa[x]][1]==x; }
inline int isroot(int x){ return !fa[x]||(son[fa[x]][0]!=x&&son[fa[x]][1]!=x); }
void Up(int u){
	if(!u) return;
	s[u]=val[u];
	if(son[u][0]) s[u]=s[u]+s[son[u][0]];
	if(son[u][1]) s[u]=s[u]+s[son[u][1]];
}
void Down(int u) {
	if(!u || !rev[u]) return;
	int t;
	if(son[u][0]) {
		t=son[u][0];
		rev[t]^=1;
		swap(son[t][0],son[t][1]);
	}
	if(son[u][1]) {
		t=son[u][1];
		rev[t]^=1;
		swap(son[t][0],son[t][1]);
	}
	rev[u]=0;
}


void rotate(int u) {
	int f=fa[u],ff=fa[f];
	int d=dir(u);
	fa[u]=ff; if(!isroot(f)) son[ff][dir(f)]=u;
	son[f][d]=son[u][!d]; if(son[u][!d]) fa[son[u][!d]]=f;
	fa[f]=u,son[u][!d]=f;
	Up(f),Up(u),Up(ff);
}
void Splay(int x) {
	static int stk[N],top=0;
	int t=x;
	while(!isroot(t)) stk[++top]=t,t=fa[t];
	stk[++top]=t;
	while(top) Down(stk[top--]);
	while(!isroot(x)) {
		int f=fa[x];
		if(!isroot(f)) {
			if(dir(x)^dir(f)) rotate(x);
			else rotate(f);
		}
		rotate(x);
	}
}

void Access(int x){ for(int t=0;x;t=x,x=fa[x]) Splay(x),son[x][1]=t,Up(x); }

void MakeRoot(int x) {
	Access(x);
	Splay(x);
	rev[x]^=1;
	swap(son[x][0],son[x][1]);
}
int GetRoot(int x) {
	Access(x),Splay(x);
	while(son[x][0]) Down(x),x=son[x][0];
	Splay(x);
	return x;
}


void Cut(int x,int y) {
	MakeRoot(x),Access(y);
	Splay(x);
	if(son[x][1]!=y || fa[y]!=x) puts("WTF Cut?");
	son[x][1]=fa[y]=0;
	Up(x);
}

struct BIT{ 
	int s[N];
	void init(){ memset(s,0,sizeof s); }
	void Add(int p,int x) {
		while(p<=n) s[p]+=x,p+=p&-p;
	}
	int Que(int p) {
		int res=0;
		while(p) res+=s[p],p-=p&-p;
		return res;
	}
} B;

void Link(int x,int y,int w) {
	if(GetRoot(x)==GetRoot(y)) {
		MakeRoot(x),Access(y);
		Splay(y);
		Node t=s[y];
		if(t.x>=w) return;
		Cut(eu[t.id],t.id);
		Cut(ev[t.id],t.id);
		B.Add(t.x,-1);
		stk[++top]=t.id;
	}
	MakeRoot(y);
	int t=stk[top--];
	eu[t]=x,ev[t]=y;
	son[t][0]=son[t][1]=rev[t]=fa[t]=0,s[t]=val[t]=(Node){w,t};
	fa[t]=x;
	fa[y]=t;
	B.Add(w,1);
}

int main(){
	while(~scanf("%d%d%d",&n,&m,&q)) {
		top=0;
		rep(i,n+1,n*2) stk[++top]=i;
		rep(i,1,n*2) fa[i]=son[i][0]=son[i][1]=rev[i]=0;
		rep(i,1,n) s[i]=val[i]=(Node){(int)1e9,0};
		rep(i,1,m) E[i].u=rd(),E[i].v=rd();
		sort(E+1,E+m+1);
		rep(i,1,q) Q[i].l=rd(),Q[i].r=rd(),Q[i].id=i;
		sort(Q+1,Q+q+1);
		int p1=1,p2=1;
		B.init();
		rep(i,1,n) {
			while(p1<=m && E[p1].u<=i&&E[p1].v<=i) {
				Link(E[p1].u,E[p1].v,min(E[p1].u,E[p1].v));
				p1++;
			}
			while(p2<=q && Q[p2].r<=i) {
				Ans[Q[p2].id]=B.Que(Q[p2].r)-B.Que(Q[p2].l-1);
				p2++;
			}
		}
		rep(i,1,q) printf("%d\n",n-Ans[i]);
	}
}



\[\ \]

\[\ \]

HDU - 5967

动态维护基环内向树

将树边直接加入,非树边更新根的\(mark\)

查询时\(GetRoot\)然后根据\(mark\)值判断

#include<cstdio>
#include<cctype>
#include<iostream>
#include<vector>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)

char IO;
int rd(){
    int s=0,f=0;
    while(!isdigit(IO=getchar())) if(IO=='-') f=1;
    do s=(s<<1)+(s<<3)+(IO^'0');
    while(isdigit(IO=getchar()));
    return f?-s:s;
}

const int N=2e5+10;

int n,m;
int fa[N],son[N][2];

inline int dir(int x){ return son[fa[x]][1]==x; }
inline int isroot(int x){ return !fa[x]||(son[fa[x]][0]!=x&&son[fa[x]][1]!=x); }
void rotate(int u){
    int f=fa[u],ff=fa[f],d=dir(u);
    fa[u]=ff; if(!isroot(f)) son[ff][dir(f)]=u;
    son[f][d]=son[u][!d]; if(son[u][!d]) fa[son[u][!d]]=f;
    fa[f]=u; son[u][!d]=f;
}
void Splay(int x) {
    while(!isroot(x)) {
        int f=fa[x];
        if(!isroot(f))  {
            if(dir(x)^dir(f)) rotate(x);
            else rotate(f);
        }
        rotate(x);
    }
}

void Access(int x){ for(int t=0;x;t=x,x=fa[x]) Splay(x),son[x][1]=t; }

int GetRoot(int x) {
    Access(x),Splay(x);
    while(son[x][0]) x=son[x][0];
    Splay(x);
    return x;
}
int mk[N];

void Link(int x,int to) {
    if(!to) return;
    if(GetRoot(x)==GetRoot(to)) {
        mk[x]=to;
        return;
    } else mk[x]=0;
    Splay(x);
    fa[x]=to;
}

void Cut(int x) {
    int rt=GetRoot(x);
    if(x==rt) {
        mk[x]=0;
        return;
    }
    Access(x);
    Splay(x);
    fa[son[x][0]]=0;
    son[x][0]=0;
    if(mk[rt]) Link(rt,mk[rt]);
}


int main(){
    n=rd(),m=rd();
    rep(i,1,n) Link(i,rd());
    rep(i,1,m) {
        int opt=rd();
        if(opt==1) {
            int x=rd();
            Cut(x);
            Link(x,rd());
        } else {
            int rt=GetRoot(rd());
            if(mk[rt]) puts("-1");
            else printf("%d\n",rt);
        }
    }
}

\[\ \]

\[\ \]

[BZOJ - 2759] 一个动态树好题

这个题其实也和上一题差不多

但是要维护路径迭代和,同时mark数组也要存非树边

#include<cstdio>
#include<cctype>
#include<iostream>
#include<vector>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
 
#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)
 
 
 
 
char IO;
int rd(){
    int s=0,f=0;
    while(!isdigit(IO=getchar())) if(IO=='-') f=1;
    do s=(s<<1)+(s<<3)+(IO^'0');
    while(isdigit(IO=getchar()));
    return f?-s:s;
}
 
const int N=2e5+10,P=1e4+7;
 
 
int n,m;
 
int fa[N],son[N][2];
 
struct Node{
    int x,y;
    Node operator + (const Node t) const {
        return (Node){x*t.x%P,(t.x*y+t.y)%P};
    }
} s[N],val[N],t[N];
 
 
 
inline int dir(int x){ return son[fa[x]][1]==x; }
inline int isroot(int x){ return !fa[x]||(son[fa[x]][1]!=x&&son[fa[x]][0]!=x); }
 
int Inv[P];
int mk[N];
 
void Up(int u) {
    if(!u) return;
    s[u]=val[u];
    if(son[u][0]) s[u]=s[son[u][0]]+s[u];
    if(son[u][1]) s[u]=s[u]+s[son[u][1]];
}
 
void rotate(int u){
    int f=fa[u],ff=fa[f],d=dir(u);
    fa[u]=ff; if(!isroot(f)) son[ff][dir(f)]=u;
    son[f][d]=son[u][!d]; if(son[u][!d]) fa[son[u][!d]]=f;
    fa[f]=u; son[u][!d]=f;
    Up(f),Up(u),Up(ff);
}
void Splay(int u) {
    while(!isroot(u)) {
        int f=fa[u];
        if(!isroot(f)) {
            if(dir(u)^dir(f)) rotate(u);
            else rotate(f);
        }
        rotate(u);
    }
}
 
void Access(int x){ for(reg int t=0;x;t=x,x=fa[x]) Splay(x),son[x][1]=t,Up(x); }
 
int GetRoot(int x) {
    Access(x);
    Splay(x);
    while(son[x][0]) x=son[x][0];
    Splay(x);
    return x;
}
 
void Link(int x,int to) {
    s[x]=val[x];
    if(GetRoot(x)==GetRoot(to)) {
        t[x]=val[x];
        s[x]=val[x]=(Node){1,0};
        mk[x]=to;
        return;
    }
    Access(x);
    Splay(x);
    fa[x]=to;
}
 
void Cut(int x) {
    if(mk[x]) {
        mk[x]=0;
        s[x]=val[x]=t[x];
        return;
    }
    int rt=GetRoot(x);
    Access(x);
    Splay(x);
    fa[son[x][0]]=0;
    son[x][0]=0;
    if(mk[rt] && GetRoot(rt)!=GetRoot(mk[rt])) {
        s[rt]=val[rt]=t[rt];
        Link(rt,mk[rt]);
        mk[rt]=0;
    }
}
 
int Solve(int x) {
    int y=mk[x];
    if(!y) return -2;
    Access(y);
    Splay(y);
    Node tmp=s[y];
    tmp=tmp+t[x];
    tmp.x=(1-tmp.x+P)%P;
    if(tmp.x==0) {
        if(tmp.y==0) return -2;
        if(tmp.y!=0) return -1;
    }
    tmp.y=tmp.y*Inv[tmp.x]%P;
    return tmp.y;
}
 
int Que(int x) {
    int rt=GetRoot(x);
    rt=Solve(rt);
    Access(x);
    Splay(x);
    Node t=s[x];
    if(t.x==0) return t.y;
    if(rt<0) return rt;
    return (t.x*rt+t.y)%P;
}
 
char opt[10];
 
int main(){
    Inv[0]=Inv[1]=1;
    rep(i,2,P-1) Inv[i]=(P-P/i)*Inv[P%i]%P;
    n=rd();
    rep(i,1,n) val[i].x=1;
    rep(i,1,n) {
        val[i].x=rd();
        int v=rd();
        val[i].y=rd();
        Link(i,v);
    }
    m=rd();
    rep(i,1,m) {
        scanf("%s",opt);
        if(opt[0]=='A') printf("%d\n",Que(rd()));
        else {
            int x=rd();
            Cut(x);
            val[x].x=rd();
            int v=rd();
            val[x].y=rd();
            Link(x,v);
        }
    }
}
 
 
 
 
 

HDU - 5467

建立状态树,在状态树上回溯更新

#include<cstdio>
#include<cctype>
#include<iostream>
#include<vector>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)
char IO;
int rd(){
	int s=0,f=0;
	while(!isdigit(IO=getchar())) if(IO=='-') f=1;
	do s=(s<<1)+(s<<3)+(IO^'0');
	while(isdigit(IO=getchar()));
	return f?-s:s;
}

const int N=3e5+10,P=1e9+7;


int n,m;
int fa[N],son[N][2],rev[N];
ll s[N],val[N];

inline int dir(int x){ return son[fa[x]][1]==x; }
inline int isroot(int x){ return !fa[x]||(son[fa[x]][0]!=x&&son[fa[x]][1]!=x); }

void Up(int u) {
	if(!u) return;
	s[u]=val[u];
	if(son[u][0]) s[u]=s[u]*s[son[u][0]]%P;
	if(son[u][1]) s[u]=s[u]*s[son[u][1]]%P;
}
void Down(int u) {
	if(!u||!rev[u]) return;
	rep(i,0,1) {
		int t=son[u][i];
		if(!t) continue;
		rev[t]^=1;
		swap(son[t][0],son[t][1]);
	}
	rev[u]=0;
}

void rotate(int u) {
	int f=fa[u],ff=fa[f],d=dir(u);
	fa[u]=ff; if(!isroot(f)) son[ff][dir(f)]=u;
	son[f][d]=son[u][!d]; if(son[u][!d]) fa[son[u][!d]]=f;
	fa[f]=u,son[u][!d]=f;
	Up(f),Up(u),Up(ff);
}
void Splay(int u) {
	static int stk[N],top=0;
	int t=u;
	Up(u);
	while(!isroot(t)) stk[++top]=t,t=fa[t];
	stk[++top]=t;
	while(top) Down(stk[top--]);
	while(!isroot(u)) {
		int f=fa[u];
		if(!isroot(f)) {
			if(dir(u)^dir(f)) rotate(u);
			else rotate(f);
		}
		rotate(u);
	}
}

void Access(int x){ for(int t=0;x;t=x,x=fa[x]) Splay(x),son[x][1]=t,Up(x); }
int GetRoot(int x){
	Access(x),Splay(x);
	while(son[x][0]) Down(x),x=son[x][0];
	Splay(x);
	return x;
}
void MakeRoot(int x) {
	Access(x),Splay(x);
	rev[x]^=1;
	swap(son[x][0],son[x][1]);
}

int suc[N];
int Cut(int x,int y) {
	if(GetRoot(x)!=GetRoot(y)) return 0;
	MakeRoot(x),Access(y);
	Splay(x);
	if(son[x][1]!=y || fa[y]!=x || son[y][0]) return 0;
	son[x][1]=fa[y]=0,Up(x);
	return 1;
}
int Link(int x,int y) {
	if(GetRoot(x)==GetRoot(y)) return 0;
	MakeRoot(y);Splay(y);
	fa[y]=x;
	return 1;
}

ll Que(int x,int y) {
	if(GetRoot(x)!=GetRoot(y)) return 0;
	MakeRoot(x);
	Access(y);
	Splay(y);
	return s[y];
}


vector <int> G[N];
int opt[N],a[N],b[N];
ll Ans[N];
void dfs(int u) {
	if(opt[u]==1) suc[u]=Link(a[u],b[u]);
	if(opt[u]==2) suc[u]=Cut(a[u],b[u]);
	if(opt[u]==4) Ans[u]=Que(a[u],b[u]);
	if(opt[u]==5) {
		suc[u]=val[a[u]],val[a[u]]=b[u]*(b[u]-1)/2;
		Splay(a[u]);
	}
	rep(i,0,G[u].size()-1) dfs(G[u][i]);
	if(opt[u]==1 && suc[u]) Cut(a[u],b[u]);
	if(opt[u]==2 && suc[u]) Link(a[u],b[u]);
	if(opt[u]==5) {
		val[a[u]]=suc[u];
		Splay(a[u]);
	}
}

int main(){
	rep(kase,1,rd()) {
		n=rd(),m=rd();
		rep(i,1,n) {
			val[i]=rd(),val[i]=val[i]*(val[i]-1)/2;
			s[i]=val[i];
			fa[i]=son[i][0]=son[i][1]=rev[i]=0;
		}
		rep(i,0,m) G[i].clear();
		int now=0;
		rep(i,1,m) {
			opt[i]=rd();
			if(opt[i]==3) now=rd();
			else a[i]=rd(),b[i]=rd();
			G[now].push_back((int)i);
			now=i;
		}
		dfs(0);
		rep(i,1,m) if(opt[i]==4) printf("%lld\n",Ans[i]);
	}
}

\[\ \]

\[\ \]

子树查询

另开一个数组,存下树上其他儿子的总和,这个维护起来比较得**

因为在很多时候都要更新,所以细节贼多,不好口述

HDU - 5405

设所有点的总和\(Sum\)

将路径拉成一条链,就是求\(Sum^2-\)链上所有节点树儿子权值平方和​

#include<cstdio>
#include<cctype>
#include<iostream>
#include<vector>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)

char IO;
int rd(){
    int s=0,f=0;
    while(!isdigit(IO=getchar())) if(IO=='-') f=1;
    do s=(s<<1)+(s<<3)+(IO^'0');
    while(isdigit(IO=getchar()));
    return f?-s:s;
}

const int N=2e5+10,P=1e9+7;

int n,m;
ll a[N];
ll ts[N],s[N],qs[N],sqs[N];
int rev[N];
ll Sum;


struct Edge{
    int to,nxt;
} e[N<<1];
int head[N],ecnt;
void AddEdge(int u,int v) {
    e[++ecnt]=(Edge){v,head[u]};
    head[u]=ecnt;
}
#define erep(u,i) for(int i=head[u];i;i=e[i].nxt)

void Mod(ll &x,ll y) {
    x+=y;
    x=(x%P+P)%P;
}

int fa[N],son[N][2];

void pre_dfs(int u,int f) {
    fa[u]=f;
    qs[u]=ts[u]=0;
    son[u][0]=son[u][1]=0;
    erep(u,i) {
        int v=e[i].to;
        if(v==f) continue;
        pre_dfs(v,u);
    }
    s[u]=(a[u]+ts[u])%P;
    Mod(ts[f],s[u]);
    Mod(qs[f],s[u]*s[u]%P);
    sqs[u]=qs[u];
}

inline int dir(int x){ return son[fa[x]][1]==x; }
inline int isroot(int x){ return !fa[x]||(son[fa[x]][1]!=x&&son[fa[x]][0]!=x); }

void Up(int u) {
    if(!u) return;
    s[u]=(ts[u]+a[u])%P;
    sqs[u]=qs[u];
    if(son[u][0]) Mod(s[u],s[son[u][0]]),Mod(sqs[u],sqs[son[u][0]]);
    if(son[u][1]) Mod(s[u],s[son[u][1]]),Mod(sqs[u],sqs[son[u][1]]);
}
void Down(int u) {
    if(!u || !rev[u]) return;
    int t;
    rep(i,0,1) {
        t=son[u][i];
        if(!t) continue;
        rev[t]^=1;
        swap(son[t][0],son[t][1]);
    }
    rev[u]=0;
}

void rotate(int u) {
    int f=fa[u],ff=fa[f],d=dir(u);
    fa[u]=ff; if(!isroot(f)) son[ff][dir(f)]=u;
    son[f][d]=son[u][!d]; if(son[u][!d]) fa[son[u][!d]]=f;
    fa[f]=u,son[u][!d]=f;
    Up(f),Up(u),Up(ff);
}
void Splay(int u) {
    static int stk[N],top=0;
    int t=u;
    while(!isroot(t)) stk[++top]=t,t=fa[t];
    stk[++top]=t;
    while(top) Down(stk[top--]);
    while(!isroot(u)) {
        int f=fa[u];
        if(!isroot(f)) {
            if(dir(u)^dir(f)) rotate(u);
            else rotate(f);
        }
        rotate(u);
    }
}

void Access(int x) {
    for(reg int t=x;t;t=fa[t]) Splay(t);
    for(reg int t=0,y=x;y;t=y,y=fa[y]) {
        if(t) {
            Mod(ts[y],-s[t]);
            Mod(qs[y],-s[t]*s[t]%P);
        }
    }
    for(reg int t=0;x;t=x,x=fa[x]) {
        Splay(x);
        if(son[x][1]) Mod(ts[x],s[son[x][1]]),Mod(qs[x],s[son[x][1]]*s[son[x][1]]%P);
        son[x][1]=t;
        Up(x);
    }
}
void MakeRoot(int x) {
    Access(x);
    Splay(x);
    rev[x]^=1;
    swap(son[x][0],son[x][1]);
}
void Upd(int x,int d) {
    Access(x);
    Splay(x);
    Sum-=a[x];
    Sum+=(a[x]=d);
    Up(x);
}

void Que(int x,int y){ 
    MakeRoot(x);
    Access(y); Splay(y);
    ll ans=(Sum%P*(Sum%P))%P-sqs[y];
    Mod(ans,0);
    printf("%lld\n",ans);
}

int main(){
    while(~scanf("%d%d",&n,&m)) {
        ecnt=Sum=0;
        rep(i,1,n) a[i]=rd(),head[i]=0,Sum+=a[i];
        rep(i,2,n) {
            int u=rd(),v=rd();
            AddEdge(u,v);
            AddEdge(v,u);
        }
        pre_dfs(1,0);
        rep(i,1,m) {
            int opt=rd();
            if(opt==1) {
                int x=rd(),w=rd();
                Upd(x,w);
            } else Que(rd(),rd());
        }
    }
}

posted @ 2019-10-14 22:02  chasedeath  阅读(279)  评论(0编辑  收藏  举报