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
函数是最主要的函数。其它还有函数makeroot
,split
等。反正,就是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;
}