LCT模板

134 篇文章 0 订阅
17 篇文章 0 订阅

此题是bzoj2631
Lct真的是看一眼就懂的数据结构,Tarjan太强了
考虑轻重链剖分,我们可以将一条路径分成log个区间
而在Lct上,用splay来完成这一过程,每次将节点到根的路径强制变为重路径(Access操作)
这样,就可以很方便的来完成链上的查询和修改了
而换根,需要维护一个Reverse标记,每次 a c c e s s ( x ) access(x) access(x)之后将整颗splay翻转
反正不是很复杂辣,和线段树差不多吧

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define M 51061
#define N 100010
#define son(x) (x==s[f[x]][1])
using namespace std;
int n,m,s[N][2],f[N],r[N];
unsigned v[N],w[N],a[N],t[N],sz[N];
inline int R(int x){
	return s[f[x]][son(x)]==x;
}
inline void ps(int x){
	sz[x]=(sz[s[x][0]]+sz[s[x][1]]+1)%M;
	w[x]=(w[s[x][0]]+w[s[x][1]]+v[x])%M;
}
inline void add(int x,int d){
	v[x]=(v[x]+d)%M; w[x]=(w[x]+d*sz[x])%M; a[x]=(a[x]+d)%M;
}
inline void mul(int x,int d){
	v[x]=v[x]*d%M; w[x]=w[x]*d%M; t[x]=t[x]*d%M; a[x]=a[x]*d%M;
}
inline void rev(int x){
	r[x]^=1; swap(s[x][0],s[x][1]);
}
inline void pd(int x){
	if(r[x]){
		rev(s[x][0]);
		rev(s[x][1]);
		r[x]=0;
	}
	if(t[x]!=1){
		mul(s[x][0],t[x]);
		mul(s[x][1],t[x]);
		t[x]=1;
	}
	if(a[x]){
		add(s[x][0],a[x]);
		add(s[x][1],a[x]);
		a[x]=0;
	}
}
inline void rot(int x){
	int p=f[x],g=f[p],d=son(x);
	f[s[p][d]=s[x][!d]]=p;
	if(R(p)) s[g][son(p)]=x; f[x]=g;
	f[s[x][!d]=p]=x; ps(p); ps(x);
}
inline void pushdown(int x){
	static int t,w[N];
	for(w[t=1]=x;R(x);x=f[x]) w[++t]=f[x];
	for(;t;--t) pd(w[t]);
}
inline void splay(int x){
	pushdown(x);
	for(int p;R(x);rot(x))
		if(R(p=f[x])&&son(x)==son(p)) rot(p);
}
inline int access(int x,int y=0){
	for(;x;y=x,x=f[x])
		splay(x),s[x][1]=y,ps(x);
	for(;s[y][0];y=s[y][0]);
	return y;
}
inline void mkrt(int x){
	access(x); splay(x); rev(x);
}
inline void link(int x,int y){
	mkrt(x);
	if(access(y)!=x) f[x]=y;
}
inline void cut(int x,int y){
	mkrt(x);
	if(access(y)==x){
		splay(y);
		if(f[x]==y && !s[x][1]){
			f[x]=s[y][0]=0; ps(y);
		}
	}
}
int main(){
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;++i) v[i]=t[i]=1;
	for(int x,y,i=1;i<n;++i){
		scanf("%d%d",&x,&y); link(x,y);
	}
	char o[3];
	for(int x,y,c;m--;){
		scanf("%s%d%d",o,&x,&y);
		if(*o=='+'){
			scanf("%d",&c);
			mkrt(x); access(y); splay(y); add(y,c);
		} else if(*o=='-'){
				cut(x,y);
			scanf("%d%d",&x,&y);
			link(x,y);
		} else if(*o=='*'){
			scanf("%d",&c);
			mkrt(x); access(y); splay(y); mul(y,c);
		} else {
			mkrt(x); access(y); splay(y); printf("%d\n",w[y]);
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值