BZOJ2631 tree【LCT】

传送门


PROBLEM

一棵n个点的树,每个点的初始权值为1。对于这棵树有q个操作,每个操作为以下四种操作之一:

  • u v c:将u到v的路径上的点的权值都加上自然数c;

− u1 v1 u2 v2:将树中原有的边(u1,v1)删除,加入一条新边(u2,v2),保证操作完之后仍然是一棵树;

∗ u v c:将u到v的路径上的点的权值都乘上自然数c;

/ u v:询问u到v的路径上的点的权值和,求出答案对于51061的余数。


DATA

1 &lt; = n , q &lt; = 1 0 5 1&lt;=n,q&lt;=10^5 1<=nq<=105 0 &lt; = c &lt; = 1 0 4 0&lt;=c&lt;=10^4 0<=c<=104


SOL

直接lct区间打标记就可以了。

注意乘法标记要影响加法的

‘还有,不该pushup的地方不要pushup,否则可能错
比如:
在这里插入图片描述
这里2号节点不会再被更新,但3,4号节点的值是错的,所以对2号pushup就错了

//splay不要写错。。


CODE

#include<bits/stdc++.h>
using namespace std;
#define sf scanf
#define pf printf
#define ll long long
const int N=1e5+10,mod=51061;
int ch[N][2],fa[N],siz[N],s[N],top,rev[N];
ll sum[N],val[N],ad[N],ml[N];
int n,Q;
#define lc(x) ch[x][0]
#define rc(x) ch[x][1]
inline bool isroot(int x){return lc(fa[x])!=x&&rc(fa[x])!=x;}
inline bool get(int x){return rc(fa[x])==x;}
inline void pushup(int x){siz[x]=siz[lc(x)]+siz[rc(x)]+1;sum[x]=sum[lc(x)]+sum[rc(x)]+val[x];sum[x]%=mod;}
inline void pushnow(int x,int _ad,int _ml){
	ad[x]=(ad[x]*_ml+_ad)%mod;
	ml[x]=(ml[x]*_ml)%mod;
	sum[x]=(sum[x]*_ml+_ad*siz[x])%mod;
	val[x]=(val[x]*_ml+_ad)%mod;
}
//加法和乘法同时出现的时候就要变形式子 
inline void pushdown(int x){
	if(rev[x]){
		swap(lc(x),rc(x));
		rev[lc(x)]^=1;rev[rc(x)]^=1;
	}
	if(ml[x]!=1||ad[x])pushnow(lc(x),ad[x],ml[x]),pushnow(rc(x),ad[x],ml[x]);
	rev[x]=ad[x]=0;ml[x]=1ll;
}
inline void rotate(int x){ 
	int y=fa[x],z=fa[y],d=get(x);
	fa[x]=z;if(!isroot(y))ch[z][get(y)]=x;
	fa[ch[x][d^1]]=y;ch[y][d]=ch[x][d^1];
	ch[x][d^1]=y;fa[y]=x;
	pushup(y);pushup(x);
}
inline void splay(int x){
	s[top=1]=x;int y=x;
	//splay写挂了就凉凉。。 
	while(!isroot(y))y=fa[y],s[++top]=y;
	while(top)pushdown(s[top--]);
	while(!isroot(x)){
		if(!isroot(fa[x]))rotate(get(x)==get(fa[x])? fa[x] : x );
		rotate(x);
	}
}
inline void access(int x){
	for(int y=0;x;y=x,x=fa[x]){
		splay(x);rc(x)=y;pushup(x);
	}
}
inline void makeroot(int x){
	access(x);splay(x);rev[x]^=1;
}
inline void split(int x,int y){
	makeroot(x);access(y);splay(y);
}
inline void link(int x,int y){
	makeroot(x);fa[x]=y;	
}
inline void cut(int x,int y){
	split(x,y);
	lc(y)=fa[x]=0;pushup(y);
}
inline ll query(int x,int y){
	split(x,y);
	return sum[y];
}
inline void change(int x,int y,int _ad,int _ml){
	split(x,y);
	pushnow(y,_ad,_ml);
}
inline void dfs(int x){if(!x)return; dfs(lc(x));cout<<x<<' ';dfs(rc(x));}
signed main(){
	sf("%d%d",&n,&Q);
	for(int i=1;i<=n;++i)siz[i]=1,val[i]=ml[i]=1ll;
	for(int i=1;i<n;++i){
		int x,y;sf("%d%d",&x,&y);link(x,y);
	}
	while(Q--){
		char c;int x,y,k,p;
		sf("\n%c%d%d",&c,&x,&y);
		if(c=='+')sf("%d",&k),change(x,y,k,1);
		if(c=='-')sf("%d%d",&k,&p),cut(x,y),link(k,p);
		if(c=='*')sf("%d",&k),change(x,y,0,k);
		if(c=='/')pf("%lld\n",query(x,y));
	}
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值