[noi2005][treap]序列维护

这道题能用辣鸡度搜出来的treap做法只有两三篇吧,并且没有详解。。。

先看数据范围,本蒟蒻感觉nlogn过不了啊。。。 [主要是我是大常数选手]

其实可以勉强过,

跪烂了那些只跑1s多的神犇%%%


首先,四百万次的插入,如果你每次都申请一个新的空间,肯定是要MLE的。

我们注意到数列中最多只有五十万个元素,也就是说我们最多同时用到五十万个空间。那么可以这样处理:维护一个栈,删除时将要删除的元素加到栈里,每次申请新的节点时先看栈里还有没有元素,如果有,那么将栈顶出栈,用于新申请的元素,否则新申请空间,,

Treap *newnode (int xxx) {
	if (top) return new (stk[top--]) Treap (xxx, Null);
	return new (pool++) Treap (xxx, Null);
}


求区间最大值的话,对于一个节点多维护如下信息,以这个节点为根的子树代表的序列,最右边一段连续数值最大和是多少,最左边一段连续数值的最大和是多少,这段区间最大的一段连续子序列和是多少。(分别记为rmax, lmax, mmax)当前节点的值记为data, 当前节点所代表的子树的元素和为sum,size代表该子树元素个数。

rmax可以这样转移 : 它等于 Max (右儿子的rmax,data + 右儿子的sum + Max (左儿子的rmax,0))。



如图,DE代表当前节点的那个元素,BC为当前节点代表的子树,BD代表当前节点的左儿子所代表的子树,EC代表当前节点的右儿子所代表的子树,如果BC这段区间的rmax的那段区间在EC中,那么它就是CE的rmax,如果不在EC中,即左端点在E的左边,那么它就等于BE这段区间的rmax加上EC这段区间的和,BE这段区间的rmax怎么求呢?首先DE必须要选(因为左端点在e左边),那么我们就可以看BD的rmax了。如果BD的rmax < 0 ,那么选了他会让BC的rmax变小,不如不选。所以可以得出上面的式子。

选右儿子同理。那么当前节点的mmax = Max (当前节点的lmax,当前节点的rmax,左儿子的mmax,右儿子的mmax,Max(0,左儿子的rmax)+Max(0,右儿子的lmax)+data)

至于为什么这样,就留给你们自己思考啦。

	void update () {
		if (l != Null) l -> pushdown ();
		if (r != Null) r -> pushdown ();
		size = l -> size + r -> size + 1;
		sum = l -> sum + r -> sum + data;
		lmax = data + std :: max (r -> lmax, 0);
		rmax = data + std :: max (l -> rmax, 0); 
		if (l != Null) lmax = std :: max (l -> lmax, l -> sum + lmax);
		if (r != Null) rmax = std :: max (r -> rmax, r -> sum + rmax);
		mmax = std :: max (std :: max (l -> mmax, r -> mmax), std :: max (std :: max(lmax, rmax), std :: max (l -> rmax, 0) + std :: max (r -> lmax, 0) + data));
	}

那么怎样下放标记呢,对于翻转的标记,把左右儿子交换,将lmax , rmax交换即可。对于覆盖的标记,就是把data改成标记的值,sum改成data*size。如果data是大于零的,那么这段区间的最大值就应该是sum。(即lmax = rmax = mmax = sum) 否则,由于至少要选一个元素,那么最大值就为data (即lmax = rmax = mmax = data)。

	void pushdown () {
		if (l1) {
			l -> l1 ^= 1; r -> l1 ^= 1; l1 ^= 1;
			std :: swap (l, r);
			std :: swap (lmax, rmax); 
		}
		if (l21) {
			l -> l21 = r -> l21 = true;
			l -> l22 = r -> l22 = l22;
			l21 = false;
			data = l22;
			sum = data * size;
			lmax = rmax = mmax = l22 > 0 ? sum : data;
		}
	}

最后上代码,反正细节挺多的。。。 我语文不好,感觉对不住观众朋友QAQ

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<string>
#include<iostream>
#include<algorithm>
#include<cctype>
#include<cmath>

const int N = 5e5 + 9;

int top, n, m;

struct Treap *Null;

struct Treap {
	int data, sum, size, hr, lmax, rmax, mmax; bool l1, l21; int l22;
	Treap *l, *r;
	Treap () {}
	Treap (int data, Treap *fl) : data(data), sum(data), size(1), hr(rand()), lmax(data), rmax(data), mmax(data), l1(0), l21(0), l(fl), r(fl) {}
	void update () {
		if (l != Null) l -> pushdown ();
		if (r != Null) r -> pushdown ();
		size = l -> size + r -> size + 1;
		sum = l -> sum + r -> sum + data;
		lmax = data + std :: max (r -> lmax, 0);
		rmax = data + std :: max (l -> rmax, 0); 
		if (l != Null) lmax = std :: max (l -> lmax, l -> sum + lmax);
		if (r != Null) rmax = std :: max (r -> rmax, r -> sum + rmax);
		mmax = std :: max (std :: max (l -> mmax, r -> mmax), std :: max (std :: max(lmax, rmax), std :: max (l -> rmax, 0) + std :: max (r -> lmax, 0) + data));
	}
	void pushdown () {
		if (l1) {
			l -> l1 ^= 1; r -> l1 ^= 1; l1 ^= 1;
			std :: swap (l, r);
			std :: swap (lmax, rmax); 
		}
		if (l21) {
			l -> l21 = r -> l21 = true;
			l -> l22 = r -> l22 = l22;
			l21 = false;
			data = l22;
			sum = data * size;
			lmax = rmax = mmax = l22 > 0 ? sum : data;
		}
	}
}*root, meme[N], *pool = meme, *stk[N];

void rec (Treap *x) {
	if (x == Null) return ;
	rec (x -> l);
	rec (x -> r);
	stk[++top] = x;
}

Treap *newnode (int xxx) {
	if (top) return new (stk[top--]) Treap (xxx, Null);
	return new (pool++) Treap (xxx, Null);
}

Treap *Merge (Treap *A, Treap *B) {
	if (A == Null) return B;
	if (B == Null) return A;
	if (A -> hr > B -> hr) {
		B -> pushdown ();
		B -> l = Merge (A, B -> l);
		B -> update ();
		return B;
	} else {
		A -> pushdown ();
		A -> r = Merge (A -> r, B);
		A -> update ();
		return A;
		
	}
}

using std :: pair;
typedef pair <Treap *, Treap *> Droot;

Droot Split (Treap *x, int k) {
	if (x == Null) return Droot (Null, Null);
	Droot y; x -> pushdown ();
	if (x -> l -> size >= k) {
		y = Split (x -> l, k);
		x -> l = y . second;
		x -> update ();
		y . second = x;
	} else {
		y = Split (x -> r, k - x -> l -> size - 1);
		x -> r = y . first;
		x -> update ();
		y . first = x;
	}
	return y;
}

int num; bool fl; char a;

int getin () {
	num = fl = 0;
	for (a = getchar (); a < '0' || a > '9'; a = getchar()) if (a == '-') fl = true;
	for (; a >= '0' && a <= '9'; a = getchar()) num = (num << 3) + (num << 1) + a - '0';
	if (fl) num = -num;
	return num;
}

Treap *Build () {
	Treap *x, *last; stk[1] = Null;
	for (int i = 1; i <= n; ++i) {
		x = new (pool++) Treap (getin (), Null); last = Null;
		while (top && stk[top] -> hr > x -> hr) {
			stk[top] -> update ();
			last = stk[top--];
		}
		if (top) stk[top] -> r = x;
		x -> l = last;
		stk[++top] = x;
	}
	while (top) stk[top--] -> update ();
	return stk[1];
}

Droot clc2, clc1;
char opt[20]; Treap *clc3;

int main () {
	Null = new Treap (-0x7fffffff, NULL); Null -> size = Null -> sum = 0;
	n = getin (); m = getin (); root = Build ();
	while (m--) {
		scanf ("%s", opt);
		if (opt[2] == 'X') printf ("%d\n", root -> mmax);
		else if (opt[2] == 'S') {
			clc1 = Split (root, getin());
			clc3 = Null;
			for (int i = getin(); i; --i) clc3 = Merge (clc3, newnode (getin()));
			root = Merge (clc1 . first, Merge (clc3, clc1 . second));
		} else {
			clc1 = Split (root, getin() - 1);
			clc2 = Split (clc1 . second, getin());
			if (opt[2] == 'K') clc2 . first -> l21 = true, clc2 . first -> l22 = getin ();
			if (opt[2] == 'V') clc2 . first -> l1 = true;
			if (opt[2] == 'T') printf ("%d\n", clc2 . first -> sum);
			if (opt[2] == 'L') rec (clc2 . first), root = Merge (clc1 . first, clc2 . second);
			else root = Merge (clc1 . first, Merge (clc2 . first, clc2 . second));
		}
	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值