数据结构基本模板复习

这就一篇简短模板博客,没有什么解说的,就是一些注意事项,准备后期写一点技巧性的东西比如线段树合并,李超线段树,动态开点, 01 01 01线段树,用于维护乘法数论,二维的树,分块,莫队,线段树优化建图, l c t lct lct之类的,看情况学习一点点其他的奇怪的树或者说图(像仙人掌这种).

模板1: 并查集

三个核心函数

int getf(int x){return fa[x]==x?x:getf(fa[x]);}
void merge(int v,int u){
	int temp1,temp2;
	temp1=getf(v); temp2=getf(u);
	if(temp2<temp1) swap(temp2,temp1);
	if(temp1!=temp2) fa[temp2]=temp1;
}
bool find(int u,int v){
	int temp1,temp2;
	temp1=getf(v); temp2=getf(u);
	return temp1==temp2;
}
  • 按秩合并:这里把 s i z e size size小的合并到的 s i z e size size大的上面去(我没写)

  • 路径压缩:其中 g e t f getf getf函数还可以进行路径压缩

int getf(int x){return fa[x]==x?x:fa[x]=getf(fa[x]);}

还能这么写

int getf(int x){
	while(x!=fa[x]) x=fa[x]=fa[fa[x]];
	return x;
}

注意要初始化哦

for(int i=1;i<=n;i++) fa[i]=i;
#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 200010
using namespace std;
int fa[maxn],n,m;
int getf(int x){return fa[x]==x?x:fa[x]=getf(fa[x]);}
void merge(int v,int u){
	int temp1,temp2;
	temp1=getf(v); temp2=getf(u);
	if(temp2<temp1) swap(temp2,temp1);
	if(temp1!=temp2) fa[temp2]=temp1;
}
bool find(int u,int v){
	int temp1,temp2;
	temp1=getf(v); temp2=getf(u);
	return temp1==temp2;
}
signed main(){
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++) fa[i]=i;
	for(int i=1,type,x,y;i<=m;i++){
		scanf("%d %d %d",&type,&x,&y);
		if(type==1) merge(x,y);
		else printf("%c\n",find(x,y)?'Y':'N');
	} 
	return 0;
}

模板2:可持久化并查集

看了一遍又打了一遍,感觉小问题还是有几个.

总体思路:用主席树维护,由于线段树动态开点所以能够大量的节省空间.不路径压缩,所以要按秩合并.

  • b u i l d build build函数:初始化,相当于 f a [ i ] = i fa[i]=i fa[i]=i,所以不能像有些线段树题不要 b u i l d build build函数
  • q u e r y query query函数:注意要返回 i d id id,因为后面在主函数中要看 d e e p deep deep的时候才能找到 t r e e tree tree的下标
  • f i n d find find函数:正常的并查集函数,不路径压缩,配合 q u e r y query query函数使用.
  • u p d a t e update update函数:正常修改函数…
  • a d d add add函数:给并查集加深度.
#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 200100
using namespace std;
int n,m,k,root[maxn],fa[maxn<<4];
struct node{
	int l,r,deep;
}tree[maxn<<4];
void build(int &id,int l,int r){
	id=++k;
	if(l==r) {fa[id]=l;return ;}
	int mid=l+r>>1;
	build(tree[id].l,l,mid); build(tree[id].r,mid+1,r);
}
int query(int id,int l,int r,int x){
	if(l==r) return id;
	int mid=l+r>>1;
	if(mid>=x) return query(tree[id].l,l,mid,x);
	return query(tree[id].r,mid+1,r,x);
}
int find(int now,int x){
	int temp=query(now,1,n,x);
	if(fa[temp]==x) return temp;
	return find(now,fa[temp]);
}
void update(int &now,int pre,int l,int r,int x,int y){
	tree[now=++k]=tree[pre];
	if(l==r) {fa[now]=y;return ;}
	int mid=l+r>>1;
	if(mid>=x) update(tree[now].l,tree[pre].l,l,mid,x,y);
	else update(tree[now].r,tree[pre].r,mid+1,r,x,y);
}
void add(int now,int l,int r,int x){
	if(l==r) {tree[now].deep++;return ;}
	int mid=l+r>>1;
	if(mid>=x) add(tree[now].l,l,mid,x);
	else add(tree[now].r,mid+1,r,x);
}
signed main(){
	scanf("%d %d",&n,&m);
	build(root[0],1,n);
	for(int i=1,x,y,opt;i<=m;i++){
		root[i]=root[i-1];
		scanf("%d",&opt);
		if(opt==1){
			scanf("%d %d",&x,&y);
			int fx=find(root[i],x),fy=find(root[i],y);
			if(fx==fy) continue;
			if(tree[fx].deep>tree[fy].deep) swap(fx,fy);
			update(root[i],root[i-1],1,n,fa[fx],fa[fy]);
			if(tree[fx].deep==tree[fy].deep) add(root[i],1,n,fa[fy]);
		}
		else if(opt==2) scanf("%d",&x),root[i]=root[x];
		else scanf("%d %d",&x,&y),printf("%d\n",find(root[i],x)==find(root[i],y));
	} 
	return 0;
}

模板3:线段树
  • 首先这个里面有两个更新的函数一个是 p u s h u p pushup pushup一个是 p u s h d o w n pushdown pushdown,其中 p u s h u p pushup pushup是儿子更新好了,用来更新父亲节点的值( s u m sum sum),然后是 p u s h d o w n pushdown pushdown,这个要麻烦一点点,就是要先弄乘标记再弄加标记,乘标记将 m u l , a d d , s u m mul,add,sum mul,add,sum全部乘一下,加标记只弄 a d d , s u m add,sum add,sum.
  • 然后是 u p d a t e update update函数,其实是可以写成一个的.然后到了指定的区间,执行和 p u s h d o w n pushdown pushdown类似的操作.
  • 只有更新的时候有 p u s h u p pushup pushup,其他时候只有 p u s h d o w n pushdown pushdown.
#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 100010
#define int long long
using namespace std;
int n,m,mod,a[maxn];
struct node{
	int sum,size,val,add,mul;
}tree[maxn<<2];
void pushup(int id) {tree[id].sum=(tree[id*2].sum+tree[id*2+1].sum)%mod;}
void build(int id,int l,int r){
	tree[id].mul=1; tree[id].size=r-l+1;
	if(l==r) {tree[id].sum=a[l]%mod;return ;}
	int mid=l+r>>1;
	build(id*2,l,mid); build(id*2+1,mid+1,r);
	pushup(id);
}
void pushdown(int id){
	if(tree[id].mul!=1){
		tree[id*2].mul=tree[id*2].mul*tree[id].mul%mod;
		tree[id*2].add=tree[id*2].add*tree[id].mul%mod;
		tree[id*2].sum=tree[id*2].sum*tree[id].mul%mod;
		tree[id*2+1].mul=tree[id*2+1].mul*tree[id].mul%mod;
		tree[id*2+1].add=tree[id*2+1].add*tree[id].mul%mod;
		tree[id*2+1].sum=tree[id*2+1].sum*tree[id].mul%mod;
		tree[id].mul=1;
	}
	if(tree[id].add){
		tree[id*2].add=(tree[id].add+tree[id*2].add)%mod;
		tree[id*2].sum=(tree[id].add*tree[id*2].size+tree[id*2].sum)%mod;
		tree[id*2+1].add=(tree[id].add+tree[id*2+1].add)%mod;
		tree[id*2+1].sum=(tree[id].add*tree[id*2+1].size+tree[id*2+1].sum)%mod;
		tree[id].add=0;
	}
}
void update_add(int id,int l,int r,int ll,int rr,int x){
	if(l==ll && r==rr){
		tree[id].sum=(tree[id].sum+tree[id].size*x)%mod;
		tree[id].add=(tree[id].add+x)%mod;
		return ;
	}
	pushdown(id);
	int mid=l+r>>1;
	if(mid>=rr) update_add(id*2,l,mid,ll,rr,x);
	else if(mid<ll) update_add(id*2+1,mid+1,r,ll,rr,x);
	else update_add(id*2,l,mid,ll,mid,x),update_add(id*2+1,mid+1,r,mid+1,rr,x);
	pushup(id);
}
void update_mul(int id,int l,int r,int ll,int rr,int x){
	if(l==ll && r==rr){
		tree[id].sum=tree[id].sum*x%mod;
		tree[id].add=tree[id].add*x%mod;
		tree[id].mul=tree[id].mul*x%mod;
		return ;
	}
	pushdown(id);
	int mid=l+r>>1;
	if(mid>=rr) update_mul(id*2,l,mid,ll,rr,x);
	else if(mid<ll) update_mul(id*2+1,mid+1,r,ll,rr,x);
	else update_mul(id*2,l,mid,ll,mid,x),update_mul(id*2+1,mid+1,r,mid+1,rr,x);
	pushup(id);
}
int query(int id,int l,int r,int ll,int rr){
	if(l==ll && r==rr) return tree[id].sum;
	pushdown(id);
	int mid=l+r>>1;
	if(mid>=rr) return query(id*2,l,mid,ll,rr);
	else if(mid<ll) return query(id*2+1,mid+1,r,ll,rr);
	return (query(id*2,l,mid,ll,mid)+query(id*2+1,mid+1,r,mid+1,rr))%mod;
}
signed main(){
	scanf("%lld %lld %lld",&n,&m,&mod);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	build(1,1,n);
	for(int i=1,opt,x,y,k;i<=m;i++){
		scanf("%lld %lld %lld",&opt,&x,&y);
		if(opt==1) scanf("%lld",&k),update_mul(1,1,n,x,y,k%mod);
		else if(opt==2) scanf("%lld",&k),update_add(1,1,n,x,y,k%mod);
		else printf("%lld\n",query(1,1,n,x,y));
	} 
	return 0;
}

模板4:主席树
  • 因为数字太大,所以用离散化,其实也可以不用离散化,反正都是动态开点嘛
  • 初始 N = 1 N=1 N=1,因为如果数组全是 0 0 0,那么 N = 0 N=0 N=0,所以这个时候 . . . ( . . 1 , N . . ) ...(..1,N..) ...(..1,N..)就会出错.
  • 还有在初始化的时候,可以一路下来 s i z e size size++,就不用 p u s h u p pushup pushup了.

这个在可持久化并查集里就用过了…

这个是离散化版本

#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 200010
using namespace std;
int n,m,k,val[maxn],ys[maxn],N=1,root[maxn];
struct node{
	int id,val;
}a[maxn];
struct Node{
	int size,l,r;
}tree[maxn<<4];
bool cmp(node a,node b) {return a.val<b.val;}
void lsh(){
	for(int i=1;i<=n;i++){
		if(a[i].val!=a[i-1].val) N++;
		val[a[i].id]=N; ys[N]=a[i].val;
	}
}
void update(int &id,int pre,int l,int r,int pos){
	id=++k; tree[id]=tree[pre]; tree[id].size++;
	if(l==r) return ;
	int mid=l+r>>1;
	if(mid>=pos) update(tree[id].l,tree[pre].l,l,mid,pos);
	else update(tree[id].r,tree[pre].r,mid+1,r,pos);
}
int query(int x,int y,int l,int r,int pos){
	if(l==r) return l;
	int temp=tree[tree[y].l].size-tree[tree[x].l].size;
	if(temp>=pos) return query(tree[x].l,tree[y].l,l,l+r>>1,pos);
	return query(tree[x].r,tree[y].r,(l+r>>1)+1,r,pos-temp);
}
signed main(){
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&a[i].val),a[i].id=i;
	sort(a+1,a+1+n,cmp); lsh();
	for(int i=1;i<=n;i++) update(root[i],root[i-1],1,N,val[i]);
	for(int i=1,left,right,ki;i<=m;i++){
		scanf("%d %d %d",&left,&right,&ki);
		printf("%d\n",ys[query(root[left-1],root[right],1,N,ki)]);
	}
	return 0;
}

接下来的是不离散化的

#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 200010
using namespace std;
int n,m,k,data[maxn],N,root[maxn];
struct Node{
	int size,l,r;
}tree[maxn<<4];
void update(int &id,int pre,int l,int r,int pos){
	id=++k; tree[id]=tree[pre]; tree[id].size++;
	if(l==r) return ;
	int mid=l+r>>1;
	if(mid>=pos) update(tree[id].l,tree[pre].l,l,mid,pos);
	else update(tree[id].r,tree[pre].r,mid+1,r,pos);
}
int query(int x,int y,int l,int r,int pos){
	if(l==r) return l;
	int temp=tree[tree[y].l].size-tree[tree[x].l].size;
	if(temp>=pos) return query(tree[x].l,tree[y].l,l,l+r>>1,pos);
	return query(tree[x].r,tree[y].r,(l+r>>1)+1,r,pos-temp);
}
signed main(){
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&data[i]),N=max(N,data[i]);
	for(int i=1;i<=n;i++) update(root[i],root[i-1],0,N,data[i]);
	for(int i=1,left,right,ki;i<=m;i++){
		scanf("%d %d %d",&left,&right,&ki);
		printf("%d\n",query(root[left-1],root[right],0,N,ki));
	}
	return 0;
}

模板5:平衡树
实现1:stl
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
int n;
struct phsnode{
	vector<int> v;
	void add(int x){v.insert(lower_bound(v.begin(),v.end(),x),x);}
	void del(int x){v.erase(lower_bound(v.begin(),v.end(),x));}
	int findrank(int x){return lower_bound(v.begin(),v.end(),x)-v.begin()+1;}
	int rank(int x){return v.at(x-1);}
	int findbefore(int x){return v.at(lower_bound(v.begin(),v.end(),x)-v.begin()-1);}
	int findafter(int x){return v.at(upper_bound(v.begin(),v.end(),x)-v.begin());}
}phs;
int main(){
	scanf("%d",&n);
	for(int i=1,opt,x;i<=n;i++){
		scanf("%d %d",&opt,&x);
		if(opt==1) phs.add(x);
		if(opt==2) phs.del(x);
		if(opt==3) printf("%d\n",phs.findrank(x));
		if(opt==4) printf("%d\n",phs.rank(x));
		if(opt==5) printf("%d\n",phs.findbefore(x));
		if(opt==6) printf("%d\n",phs.findafter(x));
	}
	return 0;
}
实现2:fhq

这个还是要会一会,之后有一个翻转区间的平衡树,如果不会 f h q fhq fhq那就比较麻烦,同时挖个坑,学习平衡树的区间动态开点.

#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 100010
using namespace std;
int n,k,root,l,r,p;
struct node{
	int key,l,r,val,size;
}tree[maxn];
int add(int x){
	tree[++k].key=rand();
	tree[k].size=1;
	tree[k].val=x;
	return k;
}
void update(int id) {tree[id].size=tree[tree[id].l].size+tree[tree[id].r].size+1;}
void split(int u,int x,int &l,int &r){
	if(!u) {l=r=0;return ;}
	if(x>=tree[u].val) l=u,split(tree[u].r,x,tree[u].r,r);
	else r=u,split(tree[u].l,x,l,tree[u].l);
	update(u);
}
int merge(int l,int r){
	if(!l||!r) return l+r;
	if(tree[l].key<=tree[r].key){
		tree[l].r=merge(tree[l].r,r);
		update(l); return l;
	}else{
		tree[r].l=merge(l,tree[r].l);
		update(r); return r;
	}
}
int kth(int u,int x){
	if(tree[tree[u].l].size+1==x) return u;
	if(tree[tree[u].l].size>=x) return kth(tree[u].l,x);
	return kth(tree[u].r,x-tree[tree[u].l].size-1);
}
signed main(){
	scanf("%d",&n);
	for(int i=1,opt,x;i<=n;i++){
		scanf("%d %d",&opt,&x);
		if(opt==1){
			split(root,x,l,r);
			root=merge(merge(l,add(x)),r);
		}
		if(opt==2){
			split(root,x,l,r);
			split(l,x-1,l,p);
			p=merge(tree[p].l,tree[p].r);
			root=merge(merge(l,p),r);
		}
		if(opt==3){
			split(root,x-1,l,r);
			printf("%d\n",tree[l].size+1);
			root=merge(l,r);
		}
		if(opt==4){
			printf("%d\n",tree[kth(root,x)].val);
		}
		if(opt==5){
			split(root,x-1,l,r);
			printf("%d\n",tree[kth(l,tree[l].size)].val);
			root=merge(l,r);
		}
		if(opt==6){
			split(root,x,l,r);
			printf("%d\n",tree[kth(r,1)].val);
			root=merge(l,r);
		}
	}
	return 0;
}
实现3:splay 暂时不太会,等到学动态树的时候再学

模板6:可持久化平衡树

这个和线段树的可持久化很像,首先有一个 r o o t [ i ] root[i] root[i]表示版本 i i i,然后 l , r , p l,r,p l,r,p是不用很多个版本的,因为后面反正都会 m e r g e merge merge的,然后主要就是操作来体现可持久化,每次 m e r g e merge merge s p l i t split split都不改变原来的树,而是重新建一颗树,并且依附于之前的树

#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 500010
using namespace std;
int n,cnt,l,r,p,root[maxn];
struct node{
	int l,r,size,val,key;
}tree[maxn<<6];
int add(int x){
	tree[++cnt].key=rand();
	tree[cnt].size=1;
	tree[cnt].val=x;
	return cnt;
}
void update(int id) {tree[id].size=tree[tree[id].l].size+tree[tree[id].r].size+1;}
void split(int u,int x,int &l,int &r){
	if(!u) {l=r=0; return ;}
	if(x>=tree[u].val){
		l=++cnt;
		tree[l]=tree[u];
		split(tree[l].r,x,tree[l].r,r);
		update(l);
	}else{
		r=++cnt;
		tree[r]=tree[u];
		split(tree[r].l,x,l,tree[r].l);
		update(r);
	}
}
int merge(int l,int r){
	if(!l||!r) return l+r;
	if(tree[l].key<tree[r].key){
		int now=++cnt;
		tree[now]=tree[l];
		tree[now].r=merge(tree[now].r,r);
		update(now); return now;
	}
	else{
		int now=++cnt;
		tree[now]=tree[r];
		tree[now].l=merge(l,tree[now].l);
		update(now); return now;
	}
}
int kth(int u,int x){
	if(tree[tree[u].l].size+1==x) return u;
	if(tree[tree[u].l].size>=x) return kth(tree[u].l,x);
	x-=tree[tree[u].l].size+1;
	return kth(tree[u].r,x);
}
signed main(){
	scanf("%d",&n);
	for(int i=1,opt,pre,x;i<=n;i++){
		scanf("%d %d %d",&pre,&opt,&x);
		root[i]=root[pre];
		if(opt==1){
			split(root[i],x,l,r);
			root[i]=merge(merge(l,add(x)),r);
		}
		if(opt==2){
			split(root[i],x,l,r);
			split(l,x-1,l,p);
			root[i]=merge(merge(l,merge(tree[p].l,tree[p].r)),r);
		}
		if(opt==3){
			split(root[i],x-1,l,r);
			printf("%d\n",tree[l].size+1);
			root[i]=merge(l,r);
		}
		if(opt==4) printf("%d\n",tree[kth(root[i],x)].val);
		if(opt==5){
			split(root[i],x-1,l,r);
			if(!l) printf("-2147483647\n");
			else printf("%d\n",tree[kth(l,tree[l].size)].val);
			root[i]=merge(l,r);
		}
		if(opt==6){
			split(root[i],x,l,r);
			if(!r) printf("2147483647\n");
			else printf("%d\n",tree[kth(r,1)].val);
			root[i]=merge(l,r);
		}
	}
	return 0;
}

模板7:文艺平衡树
#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 100010
using namespace std;
int n,m,root,l,r,p,k;
struct node{
	int key,val,l,r,size,lazy;
}tree[maxn];
int add(int x){
	tree[++k].key=rand();
	tree[k].size=1;
	tree[k].val=x;
	return k;
}
void update(int id) {tree[id].size=tree[tree[id].l].size+tree[tree[id].r].size+1;}
void pushdown(int id){
	tree[tree[id].l].lazy^=1; tree[tree[id].r].lazy^=1;
	tree[id].lazy=0; swap(tree[id].l,tree[id].r);
}
void split(int u,int x,int &l,int &r){
	if(!u) {l=r=0;return ;}
	if(tree[u].lazy) pushdown(u);
	if(tree[tree[u].l].size+1<=x) l=u,split(tree[u].r,x-tree[tree[u].l].size-1,tree[u].r,r);
	else r=u,split(tree[u].l,x,l,tree[u].l);
	update(u);
}
int merge(int l,int r){
	if(!l||!r) return l+r;
	if(tree[l].lazy) pushdown(l);
	if(tree[r].lazy) pushdown(r);
	if(tree[l].key<=tree[r].key){
		tree[l].r=merge(tree[l].r,r);
		update(l); return l;
	}else{
		tree[r].l=merge(l,tree[r].l);
		update(r); return r;
	}
}
void print(int x){
	if(tree[x].lazy) pushdown(x);
	if(tree[x].l) print(tree[x].l);
	printf("%d ",tree[x].val);
	if(tree[x].r) print(tree[x].r);
}
signed main(){
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++) root=merge(root,add(i));
	for(int i=1,x,y;i<=m;i++){
		scanf("%d %d",&x,&y);
		split(root,y,l,r); split(l,x-1,l,p);
		tree[p].lazy^=1;
		root=merge(merge(l,p),r);
	}
	print(root);
	return 0;
}

模板8:用平衡树写只有区间加的线段树
#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 300010
#define int long long
using namespace std;
int n,m,root,l,r,p;
struct node{
	int sum,key,val,size,l,r,flag;
}tree[maxn];
int cnt=0;
int add(int x){
	tree[++cnt].val=x;
	tree[cnt].size=1;
	tree[cnt].sum=x;
	tree[cnt].key=rand();
	return cnt;
}
void pushdown(int id){
	tree[tree[id].l].sum+=tree[tree[id].l].size*tree[id].flag;
	tree[tree[id].l].flag+=tree[id].flag;
	tree[tree[id].l].val+=tree[id].flag;
	tree[tree[id].r].sum+=tree[tree[id].r].size*tree[id].flag;
	tree[tree[id].r].flag+=tree[id].flag;
	tree[tree[id].r].val+=tree[id].flag;
	tree[id].flag=0;
}
void update(int id){
	tree[id].size=tree[tree[id].l].size+tree[tree[id].r].size+1;
	tree[id].sum=tree[tree[id].l].sum+tree[tree[id].r].sum+tree[id].val;
}
void split(int u,int x,int &l,int &r){
	if(!u) {l=r=0; return ;}
	if(tree[u].flag) pushdown(u);
	if(tree[tree[u].l].size+1<=x) l=u,split(tree[u].r,x-tree[tree[u].l].size-1,tree[u].r,r);
	else r=u,split(tree[u].l,x,l,tree[u].l);
	update(u);
}
int merge(int l,int r){
	if(!l||!r) return l+r;
	if(tree[l].flag) pushdown(l);
	if(tree[r].flag) pushdown(r);
	if(tree[l].key<tree[r].key){
		tree[l].r=merge(tree[l].r,r);
		update(l); return l;
	}else{
		tree[r].l=merge(l,tree[r].l);
		update(r); return r;
	}
}
signed main(){
	scanf("%lld %lld",&n,&m);
	for(int i=1,x;i<=n;i++) scanf("%lld",&x),root=merge(root,add(x));
	for(int i=1,opt,x,y,z;i<=m;i++){
		scanf("%lld %lld %lld",&opt,&x,&y);
		split(root,y,l,r); split(l,x-1,l,p);
		if(opt==1) scanf("%lld",&z),tree[p].val+=z,tree[p].flag+=z;
		else printf("%lld\n",tree[p].sum);
		root=merge(merge(l,p),r);
	}
	return 0;
}

模板8:树链剖分
#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 100100
using namespace std;
int n,m,r,p,data[maxn],head[maxn],k,deep[maxn],size[maxn],fa[maxn],top[maxn],pos[maxn],tid[maxn],Time,son[maxn];
struct node{
	int to,next;
}edge[maxn<<1];
void add(int u,int v){
	edge[++k]=(node) {v,head[u]}; head[u]=k;
	edge[++k]=(node) {u,head[v]}; head[v]=k;
}
struct Node{
	int len,lazy,sum;
}tree[maxn<<2];
void dfs1(int u,int father){
	fa[u]=father; size[u]=1;
	for(int i=head[u];i;i=edge[i].next){
		int v=edge[i].to;
		if(v==father) continue;
		deep[v]=deep[u]+1;
		dfs1(v,u);
		size[u]+=size[v];
		if(!son[u]||size[son[u]]<size[v]) son[u]=v;
	}
}
void dfs2(int u,int tp){
	top[u]=tp; pos[u]=++Time; tid[Time]=u;
	if(!son[u]) return ;
	dfs2(son[u],tp);
	for(int i=head[u];i;i=edge[i].next){
		int v=edge[i].to;
		if(v==fa[u]||v==son[u]) continue;
		dfs2(v,v);
	}
}
void build(int id,int l,int r){
	tree[id].len=r-l+1;
	if(l==r) {tree[id].sum=data[tid[l]]; return ;}
	int mid=l+r>>1;
	build(id*2,l,mid); build(id*2+1,mid+1,r);
	tree[id].sum=(tree[id*2].sum+tree[id*2+1].sum)%p;
}
void pushdown(int id){
	tree[id*2].sum=(tree[id*2].sum+tree[id*2].len*tree[id].lazy)%p;
	tree[id*2].lazy=(tree[id*2].lazy+tree[id].lazy)%p;
	tree[id*2+1].sum=(tree[id*2+1].sum+tree[id*2+1].len*tree[id].lazy)%p;
	tree[id*2+1].lazy=(tree[id*2+1].lazy+tree[id].lazy)%p;
	tree[id].lazy=0;
}
void update(int id,int l,int r,int ll,int rr,int val){
	if(l==ll && r==rr){
		tree[id].lazy=(tree[id].lazy+val)%p;
		tree[id].sum=(tree[id].sum+val*tree[id].len%p)%p;
		return ;
	}
	if(tree[id].lazy) pushdown(id);
	int mid=l+r>>1;
	if(mid>=rr) update(id*2,l,mid,ll,rr,val);
	else if(mid<ll) update(id*2+1,mid+1,r,ll,rr,val);
	else update(id*2,l,mid,ll,mid,val),update(id*2+1,mid+1,r,mid+1,rr,val);
	tree[id].sum=(tree[id*2].sum+tree[id*2+1].sum)%p;
}
void trim(int x,int y,int val){
	while(top[x]!=top[y]){
		if(deep[top[x]]<deep[top[y]]) swap(x,y);
		update(1,1,n,pos[top[x]],pos[x],val);
		x=fa[top[x]];
	}
	if(deep[x]<deep[y]) swap(x,y);
	update(1,1,n,pos[y],pos[x],val);
}
int query(int id,int l,int r,int ll,int rr){
	if(l==ll && r==rr) return tree[id].sum;
	if(tree[id].lazy) pushdown(id);
	int mid=l+r>>1;
	if(mid>=rr) return query(id*2,l,mid,ll,rr);
	else if(mid<ll) return query(id*2+1,mid+1,r,ll,rr);
	else return (query(id*2,l,mid,ll,mid)+query(id*2+1,mid+1,r,mid+1,rr))%p;
}
int find(int x,int y){
	int sum=0;
	while(top[x]!=top[y]){
		if(deep[top[x]]<deep[top[y]]) swap(x,y);
		sum=(sum+query(1,1,n,pos[top[x]],pos[x]))%p;
		x=fa[top[x]];
	}
	if(deep[x]<deep[y]) swap(x,y);
	return (sum+query(1,1,n,pos[y],pos[x]))%p;
}
signed main(){
	scanf("%d %d %d %d",&n,&m,&r,&p);
	for(int i=1;i<=n;i++) scanf("%d",&data[i]);
	for(int i=1,u,v;i<n;i++) scanf("%d %d",&u,&v),add(u,v);
	dfs1(r,r); dfs2(r,r); build(1,1,n);
	for(int i=1,x,y,z,opt;i<=m;i++){
		scanf("%d",&opt);
		if(opt==1) scanf("%d %d %d",&x,&y,&z),trim(x,y,z);
		if(opt==2) scanf("%d %d",&x,&y),printf("%d\n",find(x,y));
		if(opt==3) scanf("%d %d",&x,&z),update(1,1,n,pos[x],pos[x]+size[x]-1,z);
		if(opt==4) scanf("%d",&x),printf("%d\n",query(1,1,n,pos[x],pos[x]+size[x]-1));
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

d3ac

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值