题目大意:
维护一个树,支持以下操作:
1、链+
2、链求和
3、链求最大
4、链求最小
5、链翻转(此处的翻转是指把链上的值翻转,而树的形态不变)
题目分析:(LCT)
如果只有前四个操作就可以用LCT或者树链剖分+线段树随便维护一下就行了。
有了第五个操作就不行了。
所以我们用两个LCT,一个维护这棵树的形态,另一个维护树上所有的权值。
两个LCT的链的剖分是一样的,但是splay的形态不需要相同。
进行所有操作的时候,都要先去形态splay里查询这个点在权值splay中的位置,然后在权值splay中做所有与权值相关的操作,包括链翻转。
如果没有链翻转这个操作,两个splay中的点应该是一一对应的。
但是有了这个操作之后,权值splay中的某一条链翻转了,而形态splay并没有翻转,所以这个对应关系就不正确了。但是两条链的中序遍历序是一一对应的,链翻转之后仍然对应,所以我们不需要维护每一个点的对应关系,只需要维护链的对应关系,在找点的时候只要先在形态splay中查询这个点的中序遍历的值,再用这个值去权值线段树中找对应的点,链的对应关系只需要在形态splay中存一个对应链内的点即可。
注:在权值splay中,一条链的father指针很有可能指向的不是它真正的father,所以在Access的时候要在形态splay中先找到这条链的father,再在权值splay中找到对应的点,再把整条链的father强制赋成这个点。
在此附上给我讲这道题的大大的友链:http://blog.csdn.net/neither_nor/article/details/52244025
(↑数组党与指针党↓)
#include <cstdio>
#include <iostream>
#include <algorithm>
#define N 52000
using namespace std;
typedef long long LL;
const LL INF=0x3f3f3f3f3f3f3f3fll;
inline LL Max(LL x,LL y) { return x>y?x:y; }
inline LL Min(LL x,LL y) { return x<y?x:y; }
struct splay{
splay *ch[2],*fa,*col;
LL val,mx,mi,sum,mark,sz;
bool rev;
splay();
void maintain();
void push_down();
void push_up();
void add_mark(int);
void Reverse();
int dir();
splay* find_k(int);
}*null=new splay(),*root[N];
splay :: splay()
{
val=sum=0;
sz=null?1:0;
mx=null?0:-INF;
mi=null?0:INF;
mark=0; rev=false;
ch[0]=ch[1]=fa=col=null;
}
void splay :: maintain()
{
sz=ch[0]->sz+ch[1]->sz+1;
sum=ch[0]->sum+ch[1]->sum+val;
mx=Max(val,Max(ch[0]->mx,ch[1]->mx));
mi=Min(val,Min(ch[0]->mi,ch[1]->mi));
return;
}
void splay :: push_down()
{
if(mark)
{
ch[0]->add_mark(mark);
ch[1]->add_mark(mark);
mark=0;
}
if(rev)
{
ch[0]->Reverse();
ch[1]->Reverse();
rev=false;
}
if(col!=null)
{
if(ch[0]!=null) ch[0]->col=col;
if(ch[1]!=null) ch[1]->col=col;
}
return;
}
void splay :: push_up()
{
if(~dir()) fa->push_up();
push_down();
return;
}
void splay :: add_mark(int v)
{
if(this==null) return;
val+=v;
mx+=v;
mi+=v;
sum+=1ll*sz*v;
mark+=v;
return;
}
void splay :: Reverse()
{
if(this==null) return;
swap(ch[0],ch[1]);
rev=!rev;
return;
}
int splay :: dir()
{
return fa->ch[0]==this?0:fa->ch[1]==this?1:-1;
}
splay* splay :: find_k(int k)
{
if(this==null) return null;
push_down();
static int tmp;
tmp=ch[0]->sz+1;
if(k<tmp) return ch[0]->find_k(k);
if(k>tmp) return ch[1]->find_k(k-tmp);
return this;
}
void turn(splay *c,int d)
{
static int k;
static splay* y;
y=c->ch[d^1];
c->ch[d^1]=y->ch[d];
if(y->ch[d]!=null) y->ch[d]->fa=c;
y->ch[d]=c;
y->fa=c->fa;
if(~(k=c->dir())) c->fa->ch[k]=y;
c->fa=y;
c->maintain();
y->maintain();
return;
}
void splaying(splay *c)
{
static int d;
c->push_up();
while(~(d=c->dir()))
{
if(d==c->fa->dir()) turn(c->fa->fa,d^1);
turn(c->fa,d^1);
}
return;
}
void splaying(splay *c,splay *&_c)
{
splaying(c);
splaying(c->col);
_c=c->col->find_k(c->ch[0]->sz+1);
splaying(_c);
return;
}
int n,m,R;
int fir[N],nes[N<<1],v[N<<1],tot=1;
char s[20];
void edge(int x,int y)
{
v[++tot]=y;
nes[tot]=fir[x];
fir[x]=tot;
return;
}
#define edge(x,y) edge(x,y),edge(y,x)
void dfs(int c,int fa)
{
root[c]=new splay();
root[c]->col= new splay();
root[c]->fa=root[fa];
root[c]->col->fa=root[fa]->col;
for(int t=fir[c];t;t=nes[t])
{
if(fa==v[t]) continue;
dfs(v[t],c);
}
return;
}
void Access(splay *c)
{
static splay *_c,*y,*_y;
y=null; _y=null;
while(c!=null)
{
splaying(c,_c);
c->col=_c;
c->ch[1]->col=_c->ch[1];
c->ch[1]=y; _c->ch[1]=_y; _y->fa=_c;
c->maintain(); _c->maintain();
y=c; _y=_c;
c=c->fa;
}
return;
}
void Move_to_root(splay *c)
{
static splay *_c;
Access(c);splaying(c,_c);
c->Reverse();
_c->Reverse();
return;
}
splay* extract(splay* x,splay *y)
{
static splay *_y;
Move_to_root(x);
Access(y); splaying(y,_y);
return _y;
}
int main()
{
scanf("%d%d%d",&n,&m,&R);
for(int i=1,x,y;i<n;i++)
{
scanf("%d%d",&x,&y);
edge(x,y);
}
root[0]=null;
root[0]->col=null;
dfs(R,0);
for(int i=1,x,y,z;i<=m;i++)
{
scanf("%s",s);
switch(s[2])
{
case 'c':
scanf("%d%d%d",&x,&y,&z);
extract(root[x],root[y])->add_mark(z);
break;
case 'm':
scanf("%d%d",&x,&y);
printf("%lld\n",extract(root[x],root[y])->sum);
break;
case 'j':
scanf("%d%d",&x,&y);
printf("%lld\n",extract(root[x],root[y])->mx);
break;
case 'n':
scanf("%d%d",&x,&y);
printf("%lld\n",extract(root[x],root[y])->mi);
break;
case 'v':
scanf("%d%d",&x,&y);
extract(root[x],root[y])->Reverse();
break;
}
}
return 0;
}