【NOI2005 Sequence】序列之神Splay和Splay“基础题”Sequence

怎么说呢,两夜鏖战,一上午奋斗,终于弄出来了。

最近很萎。无奈啊。今天上午强打精神搞完了,下午小小放松(我才不堕落!只是睡觉...)

保持这种干劲啊少年~


====================特别鸣谢============================

真心的感谢Roosephu大牛,一直指导我做,我很多东西都不懂,但他很耐心的教我...他也有他自己的事...但是这么努力的帮我...


真的非常感谢...



还有DRJ.....被您的代码坑了一下,不过还是感谢代码带给我的指导与启发。非常感谢。


====================闲话少说,以下正文====================

为啥说基础题?因为做完了就会splay的基本操作了。

给大家一道思考题,

sequence+求第k大元素怎么做?

....(好吧,邓大牛说是LCT)

好吧,这就不是基本操作了。所以您懂基础题是怎么来的了吧..

关于Sequence的题解网上很多,也有各种各样的写法。我就自己的感受谈一谈吧。

首先是MAX-SUM,作为蒟蒻,理解了很久...【雾】

如下图:



然后,序列最前端和最后端设立哨兵head, tail(防止插入到NULL上去)


优化:

在 rotate 过程中不需要下放标记,不 update 当前节点。max,swap 之类的自己Define。

(详见http://blog.csdn.net/jerrydung/article/details/7952460

但是他说的还不够。还有几个很重要的。

1.内存池:

struct node
{
node *f, *c[2];
int v, maxl, maxr, maxm, s, sum;
bool rev, mrk;
}vess[maxn];
typedef node *NODE;
NODE pool[maxn];


for (int i = 0; i <= maxn; i++) pool[i] = &vess[i];


pool实际上是一个栈,删除时将要删除的子树根节点进栈。

每次从栈中取出一个元素,如果发现其子节点不是null,就把子节点进栈。

为什么要这样呢?因为空间只有32MB,所以必须写动态(当然,32MB只是考场上...不过嘛,BZOJ上也是)

c++的new和delete是很慢的,所以内存池可以很大程度上提高效率。



优化2:

插入序列时,把序列弄成一颗近似完全二叉树再插进去。

NODE build(int l, int r)
{
int m = (l + r) >> 1, d;
NODE lc = null, rc = null;
if (m > l) 
lc = build(l, m - 1);
scanf("%d", &d);
NODE t = newnode(null, d, 0);
if (m < r) 
rc = build(m + 1, r);
setc(t, 0, lc);
setc(t, 1, rc);
return update(t);
}

当你要插入一段长为tot的区间时,调用build(1, tot)即可。


惊人的是,这个优化使程序跑最大数据时快了0.4s,强烈推荐。


然后,一些细节:

1.我曾经把make-same的标记写成了(int same),有标记时same改为需要变成的值

void putsame(NODE x, int k)
{
if (x == null) return;
x -> v = k;
x -> same = k; //就是这一句
x -> sum = x -> s * k;
x -> maxl = x -> maxr = x -> maxm = max(x -> sum, k);
x -> rev = 0;
}

然后pushdown判断same是否为0

但实际上,存在把所有数字改为0的情况,然后就萎了..


2.在程序中注意null的判断,在调试时看null内元素的值有没有变为意料外的值是检查的办法之一。

3.head、tail、null的初值注意一下。

4.在新建节点时,其父节点、爷爷节点、太爷爷节点(雾)的size是不用更新的。反正插完要rotate,rotate会update,所以就无所谓了。


5.关于pushdown:roosephu说了一句话:在pushdown之后,保证该节点的值是正确的。

很有用的一句话。可以帮助理解。我刚开始想了很久..



这是AC的图。

BZOJ


本地的




下面是代码。

#include<iostream>
#include<cstdio> 
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<climits>
#define max(i, j) ({int _ = (i), __ = (j); _ > __ ? _ : __;})
#define maxn 500005
#define oo 0x3F3F3F3F
#define swap(t, i, j) ({t _ = (i); (i) = (j); (j) = _;})
#define setc(F,d,C) (((F) -> c[d] = (C)) -> f = (F))

using namespace std;

int n, tot = maxn, m;

struct node
{
	node *f, *c[2];
	int v, maxl, maxr, maxm, s, sum;
	bool rev, mrk;
}vess[maxn];

typedef node *NODE;

NODE root, null, pool[maxn], head, tail;

NODE newnode(NODE fa, int val, bool k)
{
	NODE x = pool[--tot];
	if (x -> c[0] != null) pool[tot++] = x -> c[0], x -> c[0] = null;
	if (x -> c[1] != null) pool[tot++] = x -> c[1], x -> c[1] = null;
	x -> f = fa; x -> s = 1; if (fa != null) fa -> c[k] = x;
	x -> v = x -> maxl = x -> maxr = x -> maxm = x -> sum = val;
	x -> rev =  x -> mrk = 0;
	return x;
}

NODE l, r;

NODE update(NODE x)
{
	if (x == null) return null;
	l = x -> c[0], r = x -> c[1];
	x -> s = l -> s + r -> s + 1;
	x -> sum = l -> sum + r -> sum + x -> v;
	x -> maxl = max(l -> maxl, l -> sum + x -> v + max(0, r -> maxl));
	x -> maxr = max(r -> maxr, r -> sum + x -> v + max(0, l -> maxr));
	x -> maxm = max(max(l -> maxm, r -> maxm), max(l -> maxr, 0) + x -> v + max(r -> maxl, 0));
	return x;
}

void putsame(NODE x, int k)
{
	if (x == null) return;
	x -> v = k;
	x -> mrk = 1;
	x -> sum = x -> s * k;
	x -> maxl = x -> maxr = x -> maxm = max(x -> sum, k);
	x -> rev = 0;
}

void putrev (NODE x)
{	
	x -> rev ^= 1;
	swap(int, x -> maxl, x -> maxr);
	swap(NODE, x -> c[0], x -> c[1]);
}

NODE pushdown(NODE x)
{
	if (x == null) return null;
	if (x -> mrk) putsame(x -> c[0], x -> v), putsame(x -> c[1], x -> v), x -> mrk = 0;
	if (x -> rev) putrev(x -> c[0]), putrev(x -> c[1]), x -> rev = 0;
	return x;
}

void rotate(NODE x)
{
	NODE y = x -> f, z = y -> f; int k = (x == y->c[1]);
	x -> f = z; if (z != null) z -> c[y == z -> c[1]] = x;
	if (x -> c[!k] != null) setc(y, k, x -> c[!k]); else y -> c[k] = null;
	setc(x, !k, update(y));
}

NODE splay(NODE to, NODE x)
{
	pushdown(x);
	for (pushdown(x); x -> f != to; rotate(x))
		if (x -> f -> f != to)
			rotate((x -> f -> f -> c[1] == x -> f) ^ (x->f->c[1] == x) ? x : x -> f);
	if (to == null) root = x;
	return update(x);
}

NODE find(int k, NODE to)
{
	NODE t = to;
	int w;
	while(k != (w = pushdown(t) -> c[0] -> s))
		if (k > w) k -= w + 1, t = t -> c[1];
		else t = t -> c[0];
	pushdown(t);
	return splay(to -> f, t);
}

void prepare()
{
	null = new node();
	*null = (node){null, {null, null}, -oo, -oo, -oo, -oo, 0, 0, 0, 0};
	for (int i = 0; i <= maxn; i++) pool[i] = &vess[i], pool[i] -> c[0] = pool[i] -> c[1] = null;
}

NODE build(int l, int r)
{
	int m = (l + r) >> 1, d;
	NODE lc = null, rc = null;
	if (m > l) 
		lc = build(l, m - 1);
	scanf("%d", &d);
	NODE t = newnode(null, d, 0);
	if (m < r) 
		rc = build(m + 1, r);
	setc(t, 0, lc);
	setc(t, 1, rc);
	return update(t);
}

void init()
{
	freopen("sequence.in", "r", stdin);
	freopen("sequence.out", "w", stdout);
}

int main()
{
	prepare();
	init();
	scanf("%d%d", &n, &m);
	root = build(1, n);
	head = newnode(null, 0, 0);
	head -> maxl = head -> maxr = head -> maxm = head -> v = -oo;
	tail = newnode(head, 0, 1);
	tail -> maxl = tail -> maxr = tail -> maxm = tail -> v = -oo;
	setc(tail, 0, root);
	update(tail); update(head);
	root = head;
	char s[200], c;
	int pos, total, ll;
	while (m--)
	{
		scanf("%s", s);
		c = s[2];
		if (c == 'X')
		{
			printf("%d\n", root -> maxm); continue;
		}
		scanf("%d%d", &pos, &total);
		if (c == 'S')
		{
			find(pos, root); 
			find(0, root -> c[1]);
			setc(root -> c[1], 0, build(1, total));
		}
		else
		{
			find(pos - 1, root);
			find(total, root -> c[1]);
			if (c == 'L')
			{
				pool[tot++] = root -> c[1] -> c[0];
				root -> c[1] -> c[0] = null;
			}
			else if (c == 'T')
				printf("%d\n", root -> c[1] -> c[0] -> sum);
			else if (c == 'V')
				putrev(root -> c[1] -> c[0]);
			else
		 	{
				scanf("%d", &ll);
				putsame(root -> c[1] -> c[0], ll);
			}
		}
		update(root -> c[1]); update(root);
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值