NOI2005 维修数列(splay)

原创 2015年11月21日 18:16:17

题意:写一个程序维护一个序列,支持6个操作:插入一段序列,删除一段子序列,区间统一修改为一个值,区间翻转,求区间和,求整个序列的最大非空子序列(其实求区间的最大非空子序列也可以)。序列中最多同时存在5*10^5个元素。

想法:看到区间翻转就知道是splay,但这题非常恶心,要维护大量的信息,懒标记控制不当也容易写错。

这题比较有价值的地方在于分治求区间最大和,需要像线段树的hotel一题一样维护区间靠左边,靠右边,以及整个区间的最大和,然后由子树update的时候要注意,就是splay的节点实际也带有一个值,合并的时候要考虑进来,这一点和线段树有点不同。

然后当题目涉及一个以上懒标记的时候,一定要注意多个懒标记之间关系,是先后,还是并列,还是覆盖。

然后很重要但常常被忽略的一个问题,就是懒标记的性质:是读到懒标记修改当前节点信息,还是读到懒标记修改儿子节点信息。

前者比较容易写,把区间旋转出来然后打上标记返回,后者麻烦一点,要修改那个节点的信息,再打上标记再返回。

区间翻转的rev标记很好处理,随便怎么弄一般都不会出错。但其他的,例如求区间和之类的尽量用后者,今天调了很久都WA,然后把懒标记换成后面那种就A了,可见如果使用前者而在某些地方漏掉pushdown后果很严重,而后者有些地方可以不写pushdown。

还有一点,这题需要不断地插入和删除,内存开销很蛋疼。网上很多人是用循环队列或者栈在删除操作的时候遍历子树暴力回收内存,而我建了两棵splay,一棵是序列,一棵是回收站,每次删除或者插入的时候在两棵splay之间split和merge即可。注意回收的节点很可能带有删除之前打的懒标记,下次提取的时候要清空。


#include<cstdio>
#include<cstring>
#include<algorithm>

inline void get(int &r) {
	char c, b=0; r=0;
	do{c=getchar();if(c=='-')b=1;}while (c<'0'||c>'9');
	do{r=r*10+c-'0';c=getchar();}while (c>='0'&&c<='9');
	if (b) r = -r;
}

const int MAXN = 500010;
const int inf = 1e9;
using namespace std;
#define lch(x) x->ch[0]
#define rch(x) x->ch[1]

int N, M;
struct Node {
	int sz, lmx, rmx, mx, sum, v;
	bool rev, sam; //即时生效
	Node*fa, *ch[2];
	Node () {
		lmx = rmx = mx = -inf;
		rev = sam = 0;
		sum = sz = v = 0;
	}
} nil, *NIL = &nil, *r1, *r2, nds[MAXN];
//r1为序列,r2为回收站
int arr[MAXN];

inline void upsm(Node*x, int v)
{
	x->v = v;
	x->sum = v * x->sz;
	x->mx = x->lmx = x->rmx = max(x->sum, v);
	x->sam = 1;
}
inline void uprv(Node*x)
{
	swap(lch(x), rch(x));
	swap(x->lmx, x->rmx);
	x->rev ^= 1;
}

inline void pushdown(Node*x)
{
	if (x->sam) {
		x->sam = x->rev = 0;
		if (lch(x) != NIL) upsm(lch(x), x->v);
		if (rch(x) != NIL) upsm(rch(x), x->v);
	}
	if (x->rev) {
		x->rev = 0;
		if (lch(x) != NIL) uprv(lch(x));
		if (rch(x) != NIL) uprv(rch(x));
	}
}

inline void pushup(Node*x)
{
	Node*&L = lch(x), *&R = rch(x);
	x->sz = L->sz + R->sz + 1;
	x->sum = L->sum + R->sum + x->v;
	x->lmx = max(L->lmx, L->sum + x->v + max(R->lmx, 0));
	x->rmx = max(R->rmx, R->sum + x->v + max(L->rmx, 0));
	x->mx = max(0, L->rmx) + x->v + max(0, R->lmx);
	x->mx = max(x->mx, max(L->mx, R->mx));
}

inline void rotate(Node *x)
{
	Node *y = x->fa;
	int d = (x==lch(y));
	pushdown(y), pushdown(x);
	y->ch[!d] = x->ch[d];
	if (x->ch[d] != NIL) x->ch[d]->fa = y;
	x->fa = y->fa;
	if (y->fa != NIL)
		y->fa->ch[ y->fa->ch[1]==y ] = x;
	x->ch[d] = y;
	y->fa = x;
    pushup(y);
}

void splay(Node*&r, Node*x, Node*to=NIL)
{
	pushdown(x);
	for (Node *y, *z; x->fa != to; rotate(x)) {
		y = x->fa; z = y->fa;
		if (z != to) rotate((y==lch(z))^(x==lch(y)) ? x : y);
	}
	pushup(x);
	if (to == NIL) r = x;
}

Node* getkth(Node*&r, int k, Node*to=NIL)
{
	Node*t = r;
	pushdown(t);
	for (; k != lch(t)->sz + 1; pushdown(t))
		if (k <= lch(t)->sz) t = lch(t);
		else k -= lch(t)->sz + 1, t = rch(t);
	splay(r, t, to);
	return t;
}

void merge(Node*&x, Node*&y) //合并后的树保存在x中
{
	getkth(x, x->sz);
	getkth(y, 1);
	rch(x) = y; y->fa = x; pushup(x);
}

Node*build0(int l, int r, Node*fa) //建树并填值
{
	if (l > r) return NIL;
	int mid = (l+r)>>1;
	nds[mid].ch[0] = build0(l, mid-1, &nds[mid]);
	nds[mid].ch[1] = build0(mid+1, r, &nds[mid]);
	nds[mid].v = nds[mid].sum = arr[mid];
	nds[mid].fa = fa;
	pushup(&nds[mid]);
	return &nds[mid];
}

void build1(Node*t, int k) //将已建好的二叉树填上值
{
	if (t==NIL) return;
	build1(lch(t), k);
	build1(rch(t), k+1 + lch(t)->sz);
	t->v = arr[k + lch(t)->sz + 1];
	t->sam = t->rev = 0;
	pushup(t);
}

int qsum(int pos, int tot)
{
	getkth(r1, pos);
	getkth(r1, pos + tot + 1, r1);
	pushdown(rch(r1)->ch[0]);
	return rch(r1)->ch[0]->sum;
}

int qmax()
{
	int cnt = r1->sz;
	getkth(r1, 1), getkth(r1, cnt, r1);
	pushdown(rch(r1)->ch[0]);
	return rch(r1)->ch[0]->mx;
}

void insert(int pos, int tot)
{
	for (int i = 1; i<=tot; ++i) get(arr[i]);
	r2 = getkth(r2, tot + 1);
	Node*t = r2->ch[0];
	build1(t, 0);
	getkth(r1, pos+1);
	getkth(r1, pos+2, r1);
	r1->ch[1]->ch[0] = t;
	t->fa = rch(r1);
	r2->ch[0] = NIL;
	pushup(r2);
	pushup(rch(r1)); pushup(r1);
}

void erase(int pos, int tot)
{
	getkth(r1, pos);
	getkth(r1, pos+tot+1, r1);
	Node*t = rch(r1)->ch[0];
	rch(r1)->ch[0] = NIL;
	t->fa = NIL;
	merge(r2, t);
	pushup(rch(r1)); pushup(r1);
}

void makesame(int pos, int tot, int c)
{
	getkth(r1, pos);
	getkth(r1, pos+tot+1, r1);
	Node*&t = rch(r1)->ch[0];
	upsm(t, c);
	pushdown(rch(r1));
	pushup(rch(r1)); pushup(r1);
}

void reverse(int pos, int tot)
{
	getkth(r1, pos);
	getkth(r1, pos+tot+1, r1);
	uprv(lch(rch(r1)));
	pushdown(rch(r1));
	pushup(rch(r1)); pushup(r1);
}

int main()
{
	nil.ch[0] = nil.ch[1] = nil.fa = NIL;
	get(N), get(M);
	for (int i = 1; i<=N; ++i) get(arr[i]);
	r1 = build0(0, N+1, NIL);
	r2 = build0(N+2, MAXN-2, NIL);
	char op[99];
	int a, b, c;
	for (int i = 1; i<=M; ++i) {
		scanf("%s", op);
		if (op[0] == 'I')
			get(a), get(b), insert(a, b);
		else if (op[0] == 'D')
			get(a), get(b), erase(a, b);
		else if (op[2] == 'K')
			get(a), get(b), get(c), makesame(a, b, c);
		else if (op[0] == 'R')
			get(a), get(b), reverse(a, b);
		else if (op[0] == 'G')
			get(a), get(b), printf("%d\n", qsum(a, b));
		else printf("%d\n", qmax());
	}
	return 0;
}


版权声明:本文为博主原创文章,未经博主允许不得转载。

BZOJ 1500 【NOI2005 D1T2】 维修数列 Splay

题目大意:维护一个数列支持以下操作: 这个Splay有毒,抄模板请参照这里Splay裸题 可啪(?)的题,细节真的很重要 parent指针好像乱七八糟的样子,但是没用上… 在maintain操...
  • YihAN_Z
  • YihAN_Z
  • 2016年12月10日 19:23
  • 264

【BZOJ1500】[NOI2005]维修数列 Splay

Splay终极模板题…… 很清楚,像线段树一样wei
  • eolv99
  • eolv99
  • 2014年10月04日 15:56
  • 544

[BZOJ1500][NOI2005]维修数列(Splay)

ATP并不能理解为什么有人会不喜欢指针→_→
  • FromATP
  • FromATP
  • 2016年11月09日 22:00
  • 363

BZOJ1500: [NOI2005]维修数列 Splay

1500: [NOI2005]维修数列 Time Limit: 10 Sec  Memory Limit: 64 MB Submit: 12862  Solved: 4104 [Subm...
  • Oakley_
  • Oakley_
  • 2017年01月23日 18:13
  • 230

Splay Tree(伸展树)[NOI2005]维修数列

伸展树         概述:不同于线段树的以空间换取时间,用多余的节点去存储多余的信息,来达到降低时间复杂度。SplayTree基于一种更简单的思想,为了使整个查找时间更小,被查频率高的那些条目就应...

bzoj 1500 NOI2005 维修数列 [Splay]

维修数列Time Limit: 10 Sec Memory Limit: 64 MB Submit: 11839 Solved: 3724Description Input 输入的第1 行包...

1500: [NOI2005]维修数列(Splay)

经典的题目,包含了Splay常见的操作:区间最值,区间求和,区间翻转,区间改值等。 #include #include #include #include #include #in...

BZoj 1500 [NOI2005]维修数列 (Splay 模板)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1500 debug 完这道题之后整个人都斯巴达了。。。。。。。 目测电气自动化专业这学期天...

【splay】[noi2005] bzoj1500 维修数列

hehehehehehehehehehe这道题我已经没有什么想说的了 zsl告诉我这道题他写的时候被恶心惨了 其实我写的时候觉得还蛮好写的 因为之前差不多的线段树的题 写好以后本机测cena全挂 ...

[BZOJ1500][NOI2005][Splay]维修数列

[Problem Description] [Data Structure] Splay [Analysis] Splay没话说,维护节点信息和pushdown,update与线段树有...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:NOI2005 维修数列(splay)
举报原因:
原因补充:

(最多只允许输入30个字)