[BZOJ3159]决战

XIV.[BZOJ3159]决战

你们知道吗!把一行 #define int long long 写在了一行 int 的后面然后 debug 了一整天的崩溃你知道吗!!!

我恨不得罢免了自己!

言归正传。

从某种角度来说,这是我写的第一棵树套树!虽然是邪教般的LCT套splay

首先,除了翻转操作以外的其它操作,都是LCT甚至是树剖的常规操作。关键是这个 翻转 操作。

或许你会决定这很简单,直接把链 split 出来然后打上翻转的 tag 即可。如果你真这么认为的话,可就错了。LCT中的翻转,是针对的翻转,翻转的时候不仅翻转值,连儿子也一起给你翻了去!我们要实现的,是针对的翻转。LCT维护的是树的形态,我们还需要再开一棵splay维护值域。

为了方便,我们不如让新的splay一样以深度为键值排序,并维护所有节点的值。为了区别,我们把这棵新splay叫做SPLAY。考虑将同一条链中的所有节点染成同一个颜色(这里的颜色可以是链中任意一个节点的值),并将这一条链中的所有东西打包到一棵SPLAY中。

我们考虑当LCT中进行某种操作时,对应的SPLAY会进行何种变化:

splay 操作:没有改变任何一个节点的染色,SPLAY无变化。

access 操作:我们看看它的具体代码:

inline void access(int x){
	for(register int y=0;x;x=t[y=x].fa)splay(x),rson=y,pushup(x);
}

我们在这里面改变了节点的染色!

我们强制断开了 rson ,即将以 rson 为根的子树染成某种颜色;并将以 y 为根的子树染成和以 x 为根的子树的同一种颜色!

对应的SPLAY中的操作就是:挖掉深度大于等于 rson 的所有节点,并将以 y 为根的SPLAY接上去。

这个时候,我们便看出以深度为键值的好处了:只要记录一个 size 表示子树大小,我们就只需要找出SPLAY中排名为 lson_size+1 的点,断去它的右儿子,并将 y 赋成它新的右儿子即可。

说一下下文代码中各函数的含义:

lc :LCT。

sp :SPLAY。

rt[x] :节点\(x\)被染上的颜色。

CHANGE(x,y):把以\(x\)为根的子树全都染成\(y\)颜色。

fd(x,y):找到以\(x\)为根的子树中排名为\(y\)的节点,并将其转到\(root\)

inline void access(int x){
	for(register int y=0;x;x=lc.fa[y=x]){
		lc.splay(x);
		int xx=rt[x];
		sp.splay(xx);
		xx=sp.fd(xx,lc.sz[lc.ch[x][0]]+1);
		lc.CHANGE(lc.ch[x][1],sp.ch[xx][1]);
		lc.ch[x][1]=y;
		lc.CHANGE(x,xx);
		sp.fa[sp.ch[xx][1]]=0;
		sp.ch[xx][1]=rt[y];
		sp.fa[rt[y]]=xx;
		lc.pushup(x),sp.pushup(xx);
	}
}

access函数是最主要的函数。其它还有函数makerootsplit等。反正,就是splay怎样做,SPLAY就怎样做(至少大部分情况是这样,即影响SPLAY结构或染色的操作)。

inline void makeroot(int x){
	access(x),lc.splay(x),sp.splay(rt[x]),lc.REV(x),sp.REV(rt[x]);
}
inline void split(int x,int y){
	makeroot(x),access(y),lc.splay(y),sp.splay(rt[y]);
}
inline void link(int x,int y){
	makeroot(x),lc.fa[x]=y;
}

最终代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,mn[100100],mx[100100],val[100100],sum[100100],tag[100100],rt[100100],tr[100100];
struct SPLAY{
	#define lson ch[x][0]
	#define rson ch[x][1]
	int fa[100100],ch[100100][2],sz[100100];
	bool rev[100100],tp;
	inline int identify(int x){
		if(x==ch[fa[x]][0])return 0;
		if(x==ch[fa[x]][1])return 1;
		return -1;
	}
	inline void pushup(int x){
		if(tp==0){
			mn[x]=mx[x]=sum[x]=val[x],sz[x]=1;
			if(lson)mx[x]=max(mx[x],mx[lson]),mn[x]=min(mn[x],mn[lson]),sz[x]+=sz[lson],sum[x]+=sum[lson];
			if(rson)mx[x]=max(mx[x],mx[rson]),mn[x]=min(mn[x],mn[rson]),sz[x]+=sz[rson],sum[x]+=sum[rson];			
		}else{
			sz[x]=1;
			if(lson)sz[x]+=sz[lson];
			if(rson)sz[x]+=sz[rson];
		}
	}
	inline void ADD(int x,int vv){
		if(!x)return;
		sum[x]+=vv*sz[x],mn[x]+=vv,mx[x]+=vv,tag[x]+=vv,val[x]+=vv;
	}
	inline void CHANGE(int x,int y){
		if(!x)return;
		rt[x]=tr[x]=y;
	}
	inline void REV(int x){
		if(!x)return;
		swap(lson,rson),rev[x]^=1;
	}
	inline void pushdown(int x){
		if(!tp){
			if(rev[x]){
				if(lson)REV(lson);
				if(rson)REV(rson);
				rev[x]=0;
			}
			if(lson)ADD(lson,tag[x]);
			if(rson)ADD(rson,tag[x]);
			tag[x]=0;			
		}else{
			if(rev[x]){
				if(lson)REV(lson);
				if(rson)REV(rson);
				rev[x]=0;			
			}
			if(tr[x]){
				if(lson)CHANGE(lson,tr[x]);
				if(rson)CHANGE(rson,tr[x]);
				tr[x]=0;
			}
		}
	}
	inline void rotate(int x){
		register int y=fa[x];
		register int z=fa[y];
		register int dirx=identify(x);
		register int diry=identify(y);
		register int b=ch[x][!dirx];
		if(diry!=-1)ch[z][diry]=x;fa[x]=z;
		if(b)fa[b]=y;ch[y][dirx]=b;
		fa[y]=x,ch[x][!dirx]=y;
		pushup(y),pushup(x);
	}
	inline void pushall(int x){
		if(identify(x)!=-1)pushall(fa[x]);
		pushdown(x);
	}
	inline void splay(int x){
		pushall(x);
		while(identify(x)!=-1){
			register int Fa=fa[x];
			if(identify(Fa)==-1)rotate(x);
			else if(identify(x)==identify(Fa))rotate(Fa),rotate(x);
			else rotate(x),rotate(x);
		}
	}
	int fd(int x,int k){
		while(true){
			pushdown(x);
			if(k<=sz[lson])x=lson;
			else if(k>sz[lson]+1)k-=sz[lson]+1,x=rson;
			else{splay(x);return x;}
		}
	}
	#undef lson
	#undef rson 
}sp,lc;
inline void access(int x){
	for(register int y=0;x;x=lc.fa[y=x]){
		lc.splay(x);
		int xx=rt[x];
		sp.splay(xx);
		xx=sp.fd(xx,lc.sz[lc.ch[x][0]]+1);
		lc.CHANGE(lc.ch[x][1],sp.ch[xx][1]);
		lc.ch[x][1]=y;
		lc.CHANGE(x,xx);
		sp.fa[sp.ch[xx][1]]=0;
		sp.ch[xx][1]=rt[y];
		sp.fa[rt[y]]=xx;
		lc.pushup(x),sp.pushup(xx);
	}
}
inline void makeroot(int x){
	access(x),lc.splay(x),sp.splay(rt[x]),lc.REV(x),sp.REV(rt[x]);
}
inline void split(int x,int y){
	makeroot(x),access(y),lc.splay(y),sp.splay(rt[y]);
}
inline void link(int x,int y){
	makeroot(x),lc.fa[x]=y;
}
inline int read(){
	register int x=0;
	register char c=getchar();
	while(c>'9'||c<'0')c=getchar();
	while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return x;
}
signed main(){
	n=read(),m=read(),read(),lc.tp=true;
	for(int i=1;i<=n;i++)rt[i]=i,lc.sz[i]=sp.sz[i]=1;
	for(int i=1,x,y;i<n;i++)x=read(),y=read(),link(x,y);
	for(int i=1,x,y,z;i<=m;i++){
		char s[10];
		scanf("%s",s),x=read(),y=read(),split(x,y);
		if(s[2]=='c')z=read(),sp.ADD(rt[y],z);
		if(s[2]=='m')printf("%lld\n",sum[rt[y]]);
		if(s[2]=='j')printf("%lld\n",mx[rt[y]]);
		if(s[2]=='n')printf("%lld\n",mn[rt[y]]);
		if(s[2]=='v')sp.REV(rt[y]);
	}
	return 0;
}

posted @ 2021-03-31 16:15  Troverld  阅读(69)  评论(0编辑  收藏  举报