BZOJ 3786 星系探索 Splay维护树的入栈出栈序

题目大意:给出一棵树,要求有以下这些操作:1.求出一个节点到根的点权和。2.将一个节点的父亲改变。3.将一个子树中的每一个节点都加上一个权值。


思路:LCT就不用想了,因为有子树操作。然后就是一个很神奇的东西了,就是Splay维护树的入栈出栈序。这个玩应是做了这个题之后才知道的。但是感觉真的很dio。

首先,我们先按照题意,将树建出来。然后从根开始深搜,这样一个点进入DFS函数和出DFS函数的时候就会有两个时间点,就是入栈的时间和出栈的时间。然后利用Splay维护一个序列,就是入栈出栈的顺序。在数组中入栈存正,出栈存负。这个序列有很优美的性质,每一个节点的子树就是这个点的入栈和出栈之间加的东西。两点之间的路径就是一个点的入栈到另一个点的入栈。

然后这个题就是代码题了。。


CODE:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 600010
#define WORKPATH (root->son[1]->son[0])
using namespace std;

struct SplayTree{
	long long val,sum,c,size;
	long long plus,num,_num;
	SplayTree *son[2],*father;
	
	SplayTree(int _,int __);
	SplayTree() {}
	bool Check() {
		return father->son[1] == this;
	}
	void Combine(SplayTree *a,bool dir) {
		son[dir] = a;
		a->father = this;
	}
	void Plus(int c);
	void PushUp() {
		sum = son[0]->sum + son[1]->sum + val * (plus ? 1:-1);
		size = son[0]->size + son[1]->size + 1;
		num = son[0]->num + son[1]->num + (plus == 1);
		_num = son[0]->_num + son[1]->_num + (plus == 0);
	}
	void PushDown() {
		if(c) {
			son[0]->Plus(c);
			son[1]->Plus(c);
			c = 0;
		}
	}
}none,*nil = &none,*root,*tree[MAX];
SplayTree:: SplayTree(int _,int __) {
	plus = __;
	if(__ == 1)	++num;
	if(__ == 0)	++_num;
	val = _;
	sum = _ * (plus ? 1:-1);
	size = 1;
	c = 0;
	son[0] = son[1] = nil;
}
void SplayTree:: Plus(int _) {
	if(this == nil)	return ;
	sum += _ * (num - _num);
	val += _;
	c += _;
}

int points,asks;
int head[MAX],total;
int next[MAX],aim[MAX];

int src[MAX],pos[MAX];
int from[MAX],cnt;
int p[MAX];

char c[10];

inline void Add(int x,int y)
{
	next[++total] = head[x];
	aim[total] = y;
	head[x] = total;
}

void DFS(int x)
{
	pos[++cnt] = src[x];
	from[cnt] = x;
	p[cnt] = true;
	for(int i = head[x]; i; i = next[i])
		DFS(aim[i]);
	pos[++cnt] = src[x];
	from[cnt] = x;
	p[cnt] = false;
}

void Pretreatment()
{
	nil->son[0] = nil->son[1] = nil->father = nil;
	nil->val = nil->sum = nil->c = nil->size =  0;
	p[0] = p[cnt + 1] = -1;
}

SplayTree *BuildTree(int l,int r)
{
	if(l > r)	return nil;
	int mid = (l + r) >> 1;
	SplayTree *re = new SplayTree(pos[mid],p[mid]);
	if(p[mid])	tree[from[mid] << 1] = re;
	else		tree[from[mid] << 1|1] = re;
	re->Combine(BuildTree(l,mid - 1),false);
	re->Combine(BuildTree(mid + 1,r),true);
	re->PushUp();
	return re;
}

inline void Rotate(SplayTree *a,bool dir)
{
	SplayTree *f = a->father;
	f->PushDown(),a->PushDown();
	f->son[!dir] = a->son[dir];
	f->son[!dir]->father = f;
	a->son[dir] = f;
	f->father->son[f->Check()] = a;
	a->father = f->father;
	f->father = a;
	f->PushUp();
	if(root == f)	root = a;
}

inline void Splay(SplayTree *a,SplayTree *aim)
{
	while(a->father != aim) {
		if(a->father->father == aim)
			Rotate(a,!a->Check());
		else if(!a->father->Check()) {
			if(!a->Check())
				Rotate(a->father,true),Rotate(a,true);
			else	Rotate(a,false),Rotate(a,true);
		}
		else {
			if(a->Check())
				Rotate(a->father,false),Rotate(a,false);
			else	Rotate(a,true),Rotate(a,false);
		}
	}
	a->PushUp();
}

SplayTree *Find(SplayTree *a,int k)
{
	a->PushDown();
	if(a->son[0]->size >= k)	return Find(a->son[0],k);
	k -= a->son[0]->size;
	if(k == 1)	return a;
	return Find(a->son[1],k - 1);
}

inline void SplaySeg(SplayTree *a,SplayTree *b)
{
	int size_1,size_2;
	Splay(a,nil);
	size_1 = root->son[0]->size + 1;
	Splay(b,nil);
	size_2 = root->son[0]->size + 1;
	Splay(Find(root,size_1 - 1),nil);
	Splay(Find(root,size_2 + 1),root);
}

int main()
{
	cin >> points;
	for(int x,i = 2; i <= points; ++i) {
		scanf("%d",&x);
		Add(x,i);
	}
	for(int i = 1; i <= points; ++i)
		scanf("%d",&src[i]);
	DFS(1);
	Pretreatment();
	root = BuildTree(0,cnt + 1);
	root->father = nil;
	cin >> asks;
	for(int x,y,i = 1; i <= asks; ++i) {
		scanf("%s",c);
		if(c[0] == 'Q') {
			scanf("%d",&x);
			SplaySeg(tree[1 << 1],tree[x << 1]);
			printf("%lld\n",WORKPATH->sum);
		}
		else if(c[0] == 'C') {
			scanf("%d%d",&x,&y);
			SplaySeg(tree[x << 1],tree[x << 1|1]);
			SplayTree *temp = WORKPATH;
			WORKPATH = nil;
			root->son[1]->PushUp();
			root->PushUp();
			Splay(tree[y << 1],nil);
			int k = root->son[0]->size + 1;
			Splay(Find(root,k + 1),root);
			root->son[1]->Combine(temp,false);
			root->son[1]->PushUp();
			root->PushUp();
		}
		else {
			scanf("%d%d",&x,&y);
			SplaySeg(tree[x << 1],tree[x << 1|1]);
			WORKPATH->Plus(y);
		}
	}
	return 0;
}


你可能感兴趣的:(splay,bzoj,入栈出栈序)