10
2
2010
30

Boring Sequence Manipulation 小结

都说 Splay 不仅仅是平衡树那么简单,由于它可以任意旋转,它还可以高效完成一些在区间上的操作,于是就做了几道 Splay 的题目。虽然不是很难,但很巧妙,也确实很烦,所以就取了 "Boring Sequence Manipulation" 这个标题,呵呵~ 下面进入正题。

其实 Splay 高效做区间操作的原理和线段树几乎一样,都是标记延迟,都是从根到叶,在维护的时候,也都是从叶到根的维护。只是 Splay 可以改变根和叶的关系,而线段树的根叶关系则不能改变。因而Splay 可以实现任意插入和删除结点,更为轻松地实现旋转(rotate)、翻转(reverse) 等操作。然而,这不是没有代价的,因为每次做 splay/rotate 操作时都会要求立即还原被延迟的标记,使被旋转结点能够正确地被维护,所以这样会有更大的常数。而且 Splay 的码量也较线段树大。

然而实际做题的时候,问题最大的还是标记延迟的实现,这也是写线段树的关键点。鉴于以往线段树的实践,与线段树一样,我的做法是,父结点的标记都是给儿子结点做的。也就是说,父结点的标记表示的确切意义是,父结点的信息已维护,子结点的信息未维护。这就很好地对应了 rotate 的意图,push_down 为的就是使子结点的信息得到更正,而不是父结点自己的信息得到更正。相应地,从 splay/rotate 的实现中可以看出,update 总是假定父结点的标记已传给子结点,即假定 update 之前一定做过 push_down 。于是两者结合,就可以保证正确性了。

更详细地,所有结点的信息有三类,标记信息、固有信息、子结点相关信息。其中,第一个和第三个是和线段树一致的,第三个由于经常和要求的答案有直接关系,所以也被称为答案信息。由于 splay 的结点,其本身是序列中的一个元素,所以,较线段树来说,又多了一个固有信息,即与子结点无关的信息。push_down 的作用就是维护子结点固有信息和答案信息,而 update 的作用就是根据固有信息和  新建立父子关系的子结点  的信息,维护其本身的答案信息。由于 splay 的结点,同一般的树一样,既可以看成是一个结点,也可以看成是一棵子树。因而,在从某个结点到树根路径的旋转过程中,可以到最后才 update 。因为在这条路径中,从父结点 push_down 保证了固有信息的正确性,虽然在 push_down 的过程中,答案信息是错误的,但是终将会在最后的 update 操作中得到更正。并且,对该结点的 push_down ,起到了一个传递信息的作用,保证了旋转过程中 其临时子结点 的信息的正确性,这点保证了路径上其他结点的 update 操作是正确的。又由于该结点的答案信息只与固有信息和 最后建立父子关系 的子结点的答案信息有关,所以可以省去中间过程中的 update 操作。再结合 push_down 保证了固有信息的正确性,而且 update 在 push_down 之后,满足 update 的假设,所以这样做是正确的,固有信息和答案信息的正确性都得到了保证。

最后就是 mark_xxx 函数,其作用是对于给定标记,维护某个结点的固有信息和答案信息,并做上标记。事实上 push_down 也是用它来实现的,在标记信息有多个的时候 push_down 实现对子结点做何种标记,即调用哪个 mark_xxx 函数。

另外,每次做完标记,或者单独更新(对于那些只更新单个结点的题目来说)的时候,最好把其父结点转到根的位置,保证整棵树的正确性(对于那些只更新单个结点的题目,还可以提高效率)。事实上,平常的带 _size 域实现的 Splay 平衡树的删除结点操作就是这样来完成的。

那么,了解原理之后,对于这些函数做出设定,其他的基本上就可以交给模板来完成了。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAXMEM 100010

template <class T>
struct Splay {
	struct Node {
		Node *_fa, *_ch[2];
		T _val;
		Node *_min_node;
		int _size;
		bool _rev;

		Node* get_end(int c) {			// 0 表示左端 1 表示右端
			Node* x = this;
			while (x->_ch[c]) {
				x->push_down();			// notice!
				x = x->_ch[c];
			}
			return x;
		}

		Node* get_near(int c) {
			if (_ch[c])
				return _ch[c]->get_end(1 - c);
			Node* x = this;
			while (x->_fa && x->_fa->_ch[c] == x)
				x = x->_fa;
			return x->_fa;
		}

		Node* get_succ() { return get_near(1); }
		Node* get_prev() { return get_near(0); }

		int get_size() {		// 兼容 this == 0
			if (this) return _size;
			else return 0;
		}

		void mark_node() {
			if (this) {
				_rev = !_rev;
				swap(_ch[0], _ch[1]);
			}
		}

		void push_down() {				// 向下传标记
			if (_rev) {
				_ch[0]->mark_node();
				_ch[1]->mark_node();
				_rev = false;
			}
		}

		void update() {					// 向上更新,可以为空
			_size = 1 + _ch[0]->get_size() + _ch[1]->get_size();
			_min_node = this;
			if (_ch[0] && _ch[0]->_min_node->_val < _min_node->_val)
				_min_node = _ch[0]->_min_node;
			if (_ch[1] && _ch[1]->_min_node->_val < _min_node->_val)
				_min_node = _ch[1]->_min_node;
		}
	};

	Node *_root;
	static Node *const _fa_root, _nodes[MAXMEM];

	void rotate(Node* x, int c) {    // 旋转操作,0 表示左旋,1 表示右旋
		Node* y = x->_fa;
		y->push_down(); x->push_down(); // y 在 x 之上,故先 y 后 x
		y->_ch[1 - c] = x->_ch[c];
		if (x->_ch[c])
			x->_ch[c]->_fa = y;
		x->_fa = y->_fa;
		if (y->_fa != _fa_root) {
			if (y->_fa->_ch[0] == y)
				y->_fa->_ch[0] = x;
			else
				y->_fa->_ch[1] = x;
		} else
			_root = x;
		x->_ch[c] = y;
		y->_fa = x;
		y->update();						// 维护 y
	}

	void splay(Node* x, Node *f) {    // 把结点 x 转到结点 f 下面
		x->push_down();
		while (x->_fa != f) {
			if (x->_fa->_fa == f) {
				if (x->_fa->_ch[0] == x)
					rotate(x, 1);
				else
					rotate(x, 0);
			} else {
				Node *y = x->_fa, *z = y->_fa;
				if (z->_ch[0] == y) {
					if (y->_ch[0] == x)
						rotate(y, 1), rotate(x, 1);
					else
						rotate(x, 0), rotate(x, 1);
				} else {
					if (y->_ch[1] == x)
						rotate(y, 0), rotate(x, 0);
					else
						rotate(x, 1), rotate(x, 0);
				}
			}
		}
		x->update();
	}

	void build(int a, int b) {
		_root = make_tree(a, b);
		_root->_fa = _fa_root;
	}

	static Node* make_tree(int a, int b) {
		if (a > b)
			return 0;
		Node* root = _nodes + (a + b) / 2;
		root->_rev = false;
		root->_ch[0] = make_tree(a, root - _nodes - 1);
		if (root->_ch[0])
			root->_ch[0]->_fa = root;
		root->_ch[1] = make_tree(root - _nodes + 1, b);
		if (root->_ch[1])
			root->_ch[1]->_fa = root;
		root->update();
		return root;
	}
};

template <class T>
typename Splay<T>::Node *const Splay<T>::_fa_root = 0;
	
template <class T>
typename Splay<T>::Node Splay<T>::_nodes[MAXMEM];

Splay<pair<int, int> > tree;

int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
#endif
	int n;
	while (scanf("%d", &n) != EOF) {
		if (n == 0)
			break;
		for (int i = 1; i <= n; ++i) {
			scanf("%d", &tree._nodes[i]._val.first);
			tree._nodes[i]._val.second = i;
		}
		tree._nodes[n + 1]._val = make_pair(0x7fffffff, 0x7fffffff);
		tree.build(0, n + 1);
		Splay<pair<int, int> >::Node * min_node = tree._nodes, *min_succ;
		for (int i = 1; i < n; ++i) {
			tree.splay(min_node, tree._fa_root);
			min_node = tree._root->_ch[1]->_min_node;
			tree.splay(min_node, tree._root);
			min_succ = min_node->get_succ();
			tree.splay(min_succ, tree._root);
			printf("%d ", i - 1 + min_succ->_ch[0]->get_size());
			min_succ->_ch[0]->mark_node();
		}
		printf("%d\n", n);
	}
    return 0;
}

简单的不带删除的翻转,push_down 、mark_node(以后叫 mark_rev) 和 update 写完了以后,就可以把任务交给模板了,不过要注意,由于翻转,所以 get_succ 的时候,要在路径上做维护,也就是不断地 push_down ,来得到真正的后继。不过其实这是在 get_end 里面完成的,因为已经旋转到根的下面,而且根已经被更新,所以可以直接调 get_succ ,因为它一定会调用 get_end 。所以更加严谨一些的话应该写 get_end ,不过由于意义上 get_succ 更明了,所以就写 get_succ 了。而且,对于有 _rev 这种信息的树来说,get_end 就必须要通过 push_down 来保证正确性。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAXN 100000

const int MAXMEM = MAXN * 3 + 2;

int ans[MAXMEM], tot;

template <class T>
struct Splay {
	struct Node {
		Node *_fa, *_ch[2];
		T _val;
		int _size;
		bool _rev;

		Node* get_end(int c) {			// 0 表示左端 1 表示右端
			Node* x = this;
			x->push_down();				// notice!
			while (x->_ch[c]) {
				x = x->_ch[c];
				x->push_down();			// notice!
			}
			return x;
		}

		Node* get_near(int c) {
			if (_ch[c])
				return _ch[c]->get_end(1 - c);
			Node* x = this;
			while (x->_fa && x->_fa->_ch[c] == x)
				x = x->_fa;
			return x->_fa;
		}

		Node* get_succ() { return get_near(1); }
		Node* get_prev() { return get_near(0); }

		int get_size() {		// 兼容 this == 0
			if (this) return _size;
			else return 0;
		}

		void mark_node() {
			if (this) {
				_rev = !_rev;
				swap(_ch[0], _ch[1]);
			}
		}

		void push_down() {				// 向下传标记
			if (_rev) {
				_ch[0]->mark_node();
				_ch[1]->mark_node();
				_rev = false;
			}
		}

		void update() {					// 向上更新,可以为空
			_size = 1 + _ch[0]->get_size() + _ch[1]->get_size();
		}

		void travel() {
			push_down();
			if (_ch[0]) _ch[0]->travel();
			ans[tot++] = _val;
			if (_ch[1]) _ch[1]->travel();
		}
	};

	Node *_root;
	static Node *const _fa_root, _nodes[MAXMEM];

	void rotate(Node* x, int c) {    // 旋转操作,0 表示左旋,1 表示右旋
		Node* y = x->_fa;
		y->push_down(); x->push_down(); // y 在 x 之上,故先 y 后 x
		y->_ch[1 - c] = x->_ch[c];
		if (x->_ch[c])
			x->_ch[c]->_fa = y;
		x->_fa = y->_fa;
		if (y->_fa != _fa_root) {
			if (y->_fa->_ch[0] == y)
				y->_fa->_ch[0] = x;
			else
				y->_fa->_ch[1] = x;
		} else
			_root = x;
		x->_ch[c] = y;
		y->_fa = x;
		y->update();						// 维护 y
	}

	void splay(Node* x, Node *f) {    // 把结点 x 转到结点 f 下面
		x->push_down();
		while (x->_fa != f) {
			if (x->_fa->_fa == f) {
				if (x->_fa->_ch[0] == x)
					rotate(x, 1);
				else
					rotate(x, 0);
			} else {
				Node *y = x->_fa, *z = y->_fa;
				if (z->_ch[0] == y) {
					if (y->_ch[0] == x)
						rotate(y, 1), rotate(x, 1);
					else
						rotate(x, 0), rotate(x, 1);
				} else {
					if (y->_ch[1] == x)
						rotate(y, 0), rotate(x, 0);
					else
						rotate(x, 1), rotate(x, 0);
				}
			}
		}
		x->update();
	}

	Node* find_kth(int k) {
		Node* x = _root;
		x->push_down();				// notice!
		while (k != x->_ch[0]->get_size() + 1) {
			if (k <= x->_ch[0]->get_size()) {
				x = x->_ch[0];
			} else {
				k -= x->_ch[0]->get_size() + 1;
				x = x->_ch[1];
			}
			x->push_down();			// notice!
		}
		return x;
	}

	void build(int a, int b) {
		_root = make_tree(a, b);
		_root->_fa = _fa_root;
	}

	static Node* make_tree(int a, int b) {
		if (a > b)
			return 0;
		Node* root = _nodes + (a + b) / 2;
		root->_rev = false;
		root->_ch[0] = make_tree(a, root - _nodes - 1);
		if (root->_ch[0])
			root->_ch[0]->_fa = root;
		root->_ch[1] = make_tree(root - _nodes + 1, b);
		if (root->_ch[1])
			root->_ch[1]->_fa = root;
		root->update();				// notice!
		return root;
	}

	void dispaly() {
		tot = 0;
		_root->travel();
	}
};

template <class T>
typename Splay<T>::Node *const Splay<T>::_fa_root = 0;
	
template <class T>
typename Splay<T>::Node Splay<T>::_nodes[MAXMEM];

Splay<int> tree;
char buf[100];

int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
#endif
	int n, m;
	while (scanf("%d%d", &n, &m) != EOF) {
		if (n < 0 && m < 0)
			break;
		for (int i = 1; i <= n + 1; ++i)
			tree._nodes[i]._val = i;
		tree.build(0, n + 1);
		for (int i = 0; i < m; ++i) {
			scanf("%s", buf);
			int a, b, c;
			if (buf[0] == 'C') {
				scanf("%d%d%d", &a, &b, &c);
				tree.splay(tree.find_kth(a), tree._fa_root);
				tree.splay(tree.find_kth(b + 2), tree._root);
				Splay<int>::Node* handle = tree._root->_ch[1]->_ch[0];
				tree._root->_ch[1]->_ch[0] = 0;
				tree.splay(tree._root->_ch[1], tree._fa_root);

				tree.splay(tree.find_kth(c + 1), tree._fa_root);
				tree.splay(tree._root->get_succ(), tree._root);
				tree._root->_ch[1]->_ch[0] = handle;
				handle->_fa = tree._root->_ch[1];		// notice;
				tree.splay(tree._root->_ch[1], tree._fa_root);
			} else {
				scanf("%d%d", &a, &b);
				tree.splay(tree.find_kth(a), tree._fa_root);
				tree.splay(tree.find_kth(b + 2), tree._root);
				tree._root->_ch[1]->_ch[0]->mark_node();
				tree.splay(tree._root->_ch[1], tree._fa_root);
			}
		}
		tree.dispaly();
		for (int i = 1; i <= n; ++i) {
			if (i != 1) putchar(' ');
			printf("%d", ans[i]);
		}
		putchar('\n');
	}
    return 0;
}

带删除和插入以及查询第 k 个结点的区间翻转,基本同上,find_kth 跟 get_end 一样,在 _rev 环境下需要 push_down 保证正确性。本机调试的时候忘记维护父指针了,结果爆栈。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAXN 100000

const int MAXMEM = MAXN * 2 + 1;

int pick[MAXN + 1], tot;
pair<int, int> seg[MAXN * 2 + 1];
int stot;

template <class T>
struct Splay {
	struct Node {
		Node *_fa, *_ch[2];
		T _val;
		int _size;

		Node* get_end(int c) {			// 0 表示左端 1 表示右端
			Node* x = this;
			x->push_down();				// notice!
			while (x->_ch[c]) {
				x = x->_ch[c];
				x->push_down();			// notice!
			}
			return x;
		}

		Node* get_near(int c) {
			if (_ch[c])
				return _ch[c]->get_end(1 - c);
			Node* x = this;
			while (x->_fa && x->_fa->_ch[c] == x)
				x = x->_fa;
			return x->_fa;
		}

		Node* get_succ() { return get_near(1); }
		Node* get_prev() { return get_near(0); }

		int get_size() {		// 兼容 this == 0
			if (this) return _size;
			else return 0;
		}

		void push_down() {}				// 向下传标记

		void update() {					// 向上更新,可以为空
			_size = _val.second - _val.first + 1
				+ _ch[0]->get_size() + _ch[1]->get_size();
		}
	};

	Node *_root;
	static Node *const _fa_root, _nodes[MAXMEM];

	void rotate(Node* x, int c) {    // 旋转操作,0 表示左旋,1 表示右旋
		Node* y = x->_fa;
		y->push_down(); x->push_down(); // y 在 x 之上,故先 y 后 x
		y->_ch[1 - c] = x->_ch[c];
		if (x->_ch[c])
			x->_ch[c]->_fa = y;
		x->_fa = y->_fa;
		if (y->_fa != _fa_root) {
			if (y->_fa->_ch[0] == y)
				y->_fa->_ch[0] = x;
			else
				y->_fa->_ch[1] = x;
		} else
			_root = x;
		x->_ch[c] = y;
		y->_fa = x;
		y->update();						// 维护 y
	}

	void splay(Node* x, Node *f) {    // 把结点 x 转到结点 f 下面
		x->push_down();
		while (x->_fa != f) {
			if (x->_fa->_fa == f) {
				if (x->_fa->_ch[0] == x)
					rotate(x, 1);
				else
					rotate(x, 0);
			} else {
				Node *y = x->_fa, *z = y->_fa;
				if (z->_ch[0] == y) {
					if (y->_ch[0] == x)
						rotate(y, 1), rotate(x, 1);
					else
						rotate(x, 0), rotate(x, 1);
				} else {
					if (y->_ch[1] == x)
						rotate(y, 0), rotate(x, 0);
					else
						rotate(x, 1), rotate(x, 0);
				}
			}
		}
		x->update();
	}

	Node* join(Node* r1, Node* r2) {
		if (r1 && r2) {
			Node *right_end = r1->get_end(1);
			splay(right_end, r1->_fa);
			right_end->_ch[1] = r2;
			r2->_fa = right_end;
			right_end->update();		// notice!
			return right_end;
		}
		if (r1) return r1;
		if (r2) return r2;
		return 0;
	}

	int find_kth_number(int k) {
		Node* x = _root;
		x->push_down();				// notice!
		while (
			!(x->_ch[0]->get_size() < k &&
			k <= x->_ch[0]->get_size() + x->_val.second - x->_val.first + 1)
		) {
			if (k <= x->_ch[0]->get_size()) {
				x = x->_ch[0];
			} else {
				k -= x->_ch[0]->get_size() + x->_val.second - x->_val.first + 1;
				x = x->_ch[1];
			}
			x->push_down();			// notice!
		}
		int res = x->_val.first - 1 + k - x->_ch[0]->get_size();
		splay(x, _fa_root);
		return res;
	}
	
	void delete_node(Node* nd) {
		Node* x = join(nd->_ch[0], nd->_ch[1]);
		if (nd->_fa == _fa_root) {
			_root = x;
		} else {
			if (nd->_fa->_ch[0] == nd)
				nd->_fa->_ch[0] = x;
			else
				nd->_fa->_ch[1] = x;
		}
		if (x)
			x->_fa = nd->_fa;
		if (nd->_fa)
			splay(nd->_fa, _fa_root);
	}

	void build(int a, int b) {
		_root = make_tree(a, b);
		_root->_fa = _fa_root;
	}

	static Node* make_tree(int a, int b) {
		if (a > b)
			return 0;
		Node* root = _nodes + (a + b) / 2;
		root->_val = seg[(a + b) / 2];
		root->_ch[0] = make_tree(a, root - _nodes - 1);
		if (root->_ch[0])
			root->_ch[0]->_fa = root;
		root->_ch[1] = make_tree(root - _nodes + 1, b);
		if (root->_ch[1])
			root->_ch[1]->_fa = root;
		root->update();				// notice!
		return root;
	}
};

template <class T>
typename Splay<T>::Node *const Splay<T>::_fa_root = 0;
	
template <class T>
typename Splay<T>::Node Splay<T>::_nodes[MAXMEM];

Splay<pair<int, int> > tree;
char buf[100];

int n, m;

struct Query {
	char _c;
	int _x;
} q[MAXN];

int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
#endif
	int test;
	scanf("%d", &test);
	for (int cas = 1; cas <= test; ++cas) {
		scanf("%d%d", &n, &m);

		tot = 0;
		for (int i = 0; i < m; ++i) {
			scanf("%s%d", buf, &q[i]._x);
			q[i]._c = buf[0];
			if (q[i]._c != 'R')
				pick[tot++] = q[i]._x;
		}

		sort(pick, pick + tot);
		tot = unique(pick, pick + tot) - pick;
		pick[tot] = n + 1;
		stot = 0;
		if (pick[0] != 1)
			seg[stot++] = make_pair(1, pick[0] - 1);
		for (int i = 0; i < tot; ++i) {
			seg[stot++] = make_pair(pick[i], pick[i]);
			if (pick[i] + 1 != pick[i + 1])
				seg[stot++] = make_pair(pick[i] + 1, pick[i + 1] - 1);
		}

		tree.build(0, stot - 1);
		printf("Case %d:\n", cas);
		for (int i = 0; i < m; ++i) {
			if (q[i]._c == 'T') {
				int pos = lower_bound(seg, seg + stot, make_pair(q[i]._x, q[i]._x)) - seg;
				Splay<pair<int, int> >::Node* nd = tree._nodes + pos;
				tree.delete_node(nd);
				nd->_fa = tree._fa_root;
				nd->_ch[0] = 0;	nd->_ch[1] = tree._root;
				if (tree._root)				// notice!
					tree._root->_fa = nd;
				tree._root = nd; nd->update();
			} else if (q[i]._c == 'Q') {
				int pos = lower_bound(seg, seg + stot, make_pair(q[i]._x, q[i]._x)) - seg;
				Splay<pair<int, int> >::Node* nd = tree._nodes + pos;
				tree.splay(nd, tree._fa_root);
				printf("%d\n", tree._root->_ch[0]->get_size() + 1);
			} else {	// 'R'
				printf("%d\n", tree.find_kth_number(q[i]._x));
			}
		}
	}
    return 0;
}

这题没有 push_down ,应该说更简单些吧。Splay 上的离线化操作,由于 Top 的特殊性,所以可以离散化后,根据内存的固定性,离线来做。如果 Top x 是说当前第 x 个人到前端,那么可以用动态新建结点,动态插入来实现。这题我刚开始忘记考虑删除之后,可能会得到空树了,结果 RE =          =... 之前都是 1Y 的,没想到在这题上被打破了。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAXMEM 500002

int arr[MAXMEM];

template <class T>
struct Splay {
	struct Node {
		Node *_fa, *_ch[2];
		T _val, _clear_val, _sum, _max_rsum, _max_lsum, _max_sum;
		int _size;		// _size 可以不要,标记这里加
		bool _rev, _clr;

		int get_size() {		// 兼容 this == 0
			if (this) return _size;
			else return 0;
		}

		int get_sum() {
			if (this) return _sum;
			else return 0;
		}

		int get_max_lsum() {
			if (this) return _max_lsum;
			else return 0;
		}

		int get_max_rsum() {
			if (this) return _max_rsum;
			else return 0;
		}

		Node() {}
		Node(const T& x) { set_value(x); }

		void *operator new(size_t) {
			if (_stop != 0) return _st[--_stop];
			else return _nodes + _mtot++;
		}

		void operator delete(void* p, size_t) {
			_st[_stop++] = (Node*)p;
		}

		void recycle() {
			if (_ch[0]) _ch[0]->recycle();
			if (_ch[1]) _ch[1]->recycle();
			delete this;
		}

		void set_value(const T& x) {
			_val = x; _size = 1;
			_rev = _clr = false;
			_sum = _max_sum = x;
			_max_lsum = max(0, x);
			_max_rsum = max(0, x);
			_ch[0] = _ch[1] = 0;
			_fa = _fa_root;
		}

		void mark_rev() {
			if (this && !_clr) {
				_rev = !_rev;
				swap(_ch[0], _ch[1]);
				swap(_max_lsum, _max_rsum);
			}
		}

		void mark_clear(const T& v) {
			if (this) {
				_clr = true;
				_clear_val = _val = v;			// notice!
				_sum = v * _size;
				_max_lsum = max(0, v * _size);
				_max_rsum = max(0, v * _size);
				_max_sum = max(v, v * _size);
				_rev = false;
			}
		}

		void push_down() {				// 向下传标记
			if (_rev) {
				_ch[0]->mark_rev();
				_ch[1]->mark_rev();
				_rev = false;
			} else if(_clr) {
				_ch[0]->mark_clear(_clear_val);
				_ch[1]->mark_clear(_clear_val);
				_clr = false;
			}
		}

		void update_size() {
			_size = 1 + _ch[0]->get_size() + _ch[1]->get_size();
		}

		void update_sum() {
			_sum = _val + _ch[0]->get_sum() + _ch[1]->get_sum();
		}

		void update_max_sum() {
			_max_sum = _ch[0]->get_max_rsum() + _val + _ch[1]->get_max_lsum();
			if (_ch[0]) _max_sum = max(_max_sum, _ch[0]->_max_sum);
			if (_ch[1]) _max_sum = max(_max_sum, _ch[1]->_max_sum);
		}

		void update_max_lsum() {
			_max_lsum = max(_ch[0]->get_max_lsum(), _ch[0]->get_sum() + _val + _ch[1]->get_max_lsum());
		}

		void update_max_rsum() {
			_max_rsum = max(_ch[1]->get_max_rsum(), _ch[1]->get_sum() + _val + _ch[0]->get_max_rsum());
		}

		void update() {					// 向上更新
			update_size();
			update_sum();
			update_max_sum();
			update_max_lsum();
			update_max_rsum();
		}
	};

	Node *_root;
	static Node *const _fa_root, _nodes[MAXMEM], *_st[MAXMEM];
	static int _mtot, _stop;

	void rotate(Node* x, int c) {    // 旋转操作,0 表示左旋,1 表示右旋
		Node* y = x->_fa;
		y->push_down(); x->push_down(); // y 在 x 之上,故先 y 后 x
		y->_ch[1 - c] = x->_ch[c];
		if (x->_ch[c])
			x->_ch[c]->_fa = y;
		x->_fa = y->_fa;
		if (y->_fa != _fa_root) {
			if (y->_fa->_ch[0] == y)
				y->_fa->_ch[0] = x;
			else
				y->_fa->_ch[1] = x;
		} else
			_root = x;
		x->_ch[c] = y;
		y->_fa = x;
		y->update();						// 维护 y
	}

	void splay(Node* x, Node *f) {    // 把结点 x 转到结点 f 下面
		x->push_down();
		while (x->_fa != f) {
			if (x->_fa->_fa == f) {
				if (x->_fa->_ch[0] == x)
					rotate(x, 1);
				else
					rotate(x, 0);
			} else {
				Node *y = x->_fa, *z = y->_fa;
				if (z->_ch[0] == y) {
					if (y->_ch[0] == x)
						rotate(y, 1), rotate(x, 1);
					else
						rotate(x, 0), rotate(x, 1);
				} else {
					if (y->_ch[1] == x)
						rotate(y, 0), rotate(x, 0);
					else
						rotate(x, 1), rotate(x, 0);
				}
			}
		}
		x->update();
	}

	Node* find_kth(int k) {
		Node* x = _root;
		x->push_down();				// notice!
		while (k != x->_ch[0]->get_size() + 1) {
			if (k <= x->_ch[0]->get_size()) {
				x = x->_ch[0];
			} else {
				k -= x->_ch[0]->get_size() + 1;
				x = x->_ch[1];
			}
			x->push_down();			// notice!
		}
		return x;
	}

	void build(int a, int b) {
		_root = make_tree(a, b);
		if (_root)
			_root->_fa = _fa_root;
	}

	static Node* make_tree(int a, int b) {
		if (a > b)
			return 0;
		int mid = (a + b) / 2;
		Node* root = new Node(arr[mid]);
		root->_ch[0] = make_tree(a, mid - 1);
		if (root->_ch[0])
			root->_ch[0]->_fa = root;
		root->_ch[1] = make_tree(mid + 1, b);
		if (root->_ch[1])
			root->_ch[1]->_fa = root;
		root->update();				// notice!
		return root;
	}

	void clear() {
		_root = 0;
	}

	static void mem_init() {
		_mtot = _stop = 0;
	}
};

template <class T>
typename Splay<T>::Node *const Splay<T>::_fa_root = 0;

template <class T>
typename Splay<T>::Node Splay<T>::_nodes[MAXMEM];

template <class T>
typename Splay<T>::Node* Splay<T>::_st[MAXMEM];

template <class T>
int Splay<T>::_stop = 0;

template <class T>
int Splay<T>::_mtot = 0;

int n, m;

Splay<int> tree;

char buf[100];

inline void pre_operation(int s, int e) {
	Splay<int>::Node *pa = tree.find_kth(s), *pb = tree.find_kth(e);
	tree.splay(pa, tree._fa_root);
	tree.splay(pb, tree._root);
}

inline void post_operation() {
	tree.splay(tree._root->_ch[1], tree._fa_root);
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
#endif
	int test;
	scanf("%d", &test);
	for (int cas = 1; cas <= test; ++cas) {
		tree.mem_init();
		scanf("%d%d", &n, &m);
		for (int i = 1; i <= n; ++i)
			scanf("%d", arr + i);
		tree.build(0, n + 1);
		for (int i = 0; i < m; ++i) {
			scanf("%s", buf);
			int k, t, v;
			switch (buf[0]) {
			case 'M':
				if (buf[2] == 'K') {	// MAKE-SAME
					scanf("%d%d%d", &k, &t, &v);
					pre_operation(k, k + t + 1);
					tree._root->_ch[1]->_ch[0]->mark_clear(v);
					post_operation();
				} else {				// MAX-SUM
					pre_operation(1, tree._root->get_size());
					printf("%d\n", tree._root->_ch[1]->_ch[0]->_max_sum);
				}
				break;
			case 'I': {					// INSERT
					scanf("%d%d", &k, &t);
					pre_operation(k + 1, k + 2);
					for (int i = 0; i < t; ++i)
						scanf("%d", arr + i);
					Splay<int>::Node* h = Splay<int>::make_tree(0, t - 1);
					tree._root->_ch[1]->_ch[0] = h;
					if (h) h->_fa = tree._root->_ch[1];
					post_operation();
				}
				break;
			case 'D': {					// DELETE
					scanf("%d%d", &k, &t);
					pre_operation(k, k + t + 1);
					Splay<int>::Node* h = tree._root->_ch[1]->_ch[0];
					tree._root->_ch[1]->_ch[0] = 0;
					if (h) h->recycle();
					post_operation();
				}
				break;
			case 'R': {					// REVERSE
					scanf("%d%d", &k, &t);
					pre_operation(k, k + t + 1);
					tree._root->_ch[1]->_ch[0]->mark_rev();
					post_operation();
				}
				break;
			case 'G': {					// GET-SUM
					scanf("%d%d", &k, &t);
					pre_operation(k, k + t + 1);
					printf("%d\n", tree._root->_ch[1]->_ch[0]->get_sum());
				}
				break;
			}
		}
	}
	return 0;
}

比较麻烦的题,两种标记,_clr 和 _rev 都有,然后还有插入删除,以及最最ws的 MAX-SUM 询问,最后加起来,结点内信息的个数就相当恐怖了。再加上是在十分疲劳的状态下写的,于是 RE 和 WA了 N 次之后,终于 AC 。首先,这题的数据里,有一个 GET-SUM 的 tot 是 0 ,会导致非法解引用,找出这个错误,花了大概 N/3 个 RE 。之后一直 WA ,最后发现,原来是 mark_clear 里面,_val 忘记更新了,囧~ 这个错误真是太水了。这题最麻烦的还是两个标记的处理,我的做法是延续线段树时代的做法,由于 _clr 的特殊性质,每次保证标记信息中 _clr 和 _rev 里面只有一个为 true ,然后就好处理了。还有就是在 DELETE 那个地方,我纠结了好久,终于决定$O(t)$的方式返还内存了。开始是想$O(1)$返还内存的,但是想到普通实现用 new 的时候就会 $O(tlogh)$ ,觉得得不偿失,最后没有采用。忽然想到一种 del 操作 $O(1)$ new 操作 $O(t+log h)$的实现,不过比较麻烦,那就以后实现吧。

最后有个小插曲,我发现,在 SPOJ 上 AC 的代码,在衡阳八中 OJ 上,居然 WA 了。一开始以为是数据错了,搞了半天发现,果然是数据问题,SPOJ 把数据合并了,开头有个 test ,而衡阳八中 OJ 是原始 NOI2005 数据,开头没有 test 。做到这里,我差点晕过去,果然状态不好的时候不应该碰这种繁琐题的 =           =...

ps. 今天还有个插曲,我居然想不通如何维护乘法和加法的标记延迟了,囧~ 这什么状态,最后经大神提醒,终于知道怎么搞了。唉,看来今天是比较悲剧的。。。

Category: 普罗格莱明 | Tags: Splay Sequence | Read Count: 1651
seo service UK 说:
2024年5月11日 23:31

Thanks for a wonderful share. Your article has proved your hard work and experience you have got in this field. Brilliant .i love it reading . Pretty good post. I just stumbled upon your blog and wanted to say that I have really enjoyed reading your blog posts. Any way I'll be subscribing to your feed and I hope you post again soon. Big thanks for the useful info. I really thank you for the valuable info on this great subject and look forward to more great posts. Thanks a lot for enjoying this beauty article with me

먹튀폴리스주소 说:
2024年5月26日 05:43

Faast Pharmacy proudly serves as an official supplier of medical products, partnering with international pharmaceutical companies that deliver licensed medications worldwide. By establishing direct collaborations with manufacturers, we provide medications at wholesale prices, enabling our customers to save more than 100% off the original drug costs.

카지노탐구생활 说:
2024年5月26日 14:05

Elevate your beverage game with the pinnacle of excellence - the Sandjest personalized insulated tumbler! Craft your drinks to perfectly suit your unique taste buds, all while exuding an undeniable touch of refinement. Get ready to effortlessly keep your beverages at the perfect temperature, leaving a lasting impact. Upgrade your sipping journey today!

메이저놀이터추천 说:
2024年5月26日 14:06

I simply needed to thank you very much all over again. I’m not certain the things I might have made to happen without the creative concepts shown by you over my topic. It has been a very scary case in my opinion, however , noticing this specialised strategy you treated the issue forced me to jump for joy. Now i’m grateful for this information and even expect you find out what a powerful job you were putting in educating others with the aid of your web blog. I am sure you haven’t encountered any of us.

사설토토사이트 说:
2024年5月26日 14:07

Faast Pharmacy proudly serves as an official supplier of medical products, partnering with international pharmaceutical companies that deliver licensed medications worldwide. By establishing direct collaborations with manufacturers, we provide medications at wholesale prices, enabling our customers to save more than 100% off the original drug costs.

안전놀이터가입 说:
2024年5月26日 14:07

The Gems Bonanza slot from the online slot site takes players into the sparkling world of shining diamonds and precious stones. Its richly colored graphic design and smooth animations create an exciting and engaging atmosphere. The game's reels are decorated with a variety of precious stones, including emeralds, sapphires, topaz, rubies, and amethysts. The beautiful background with bright colors adds a luxurious feel to this game.

먹튀검증 说:
2024年5月26日 14:07

White wine is exceedingly regarded for purchasing very sensual and classy alcoholic drink. It is especially available in yellow or golden color. White wine is extensively liked all throughout the continents due to its scrumptious flavour. One can anytime pair white wine with chicken so that you can produce a mild meal looks scrumptious. But, to assist all you starters for higher wine merging we’ve prepared this white wine chef for starters.

안전토토사이트추천 说:
2024年5月26日 14:08

Nice placed up. I discover some thing greater difficult on numerous blogs ordinary. Most normally it is stimulating to take a look at content from different writers and use a hint something from their internet site on line. I’d prefer to use some at the equal time as the use of content by myself weblog regardless of whether you do now not mind. Natually I’ll provide link for your net blog. Many thanks sharing.

토토사이트 说:
2024年5月26日 14:08

I’ve learned a few vital matters via your put up. I’d in my opinion also like to mention that there may be a state of affairs that you may attain a mortgage and in no manner need a co-signer which includes a Fed Student Aid Loan. In case you have emerge as financing thru a everyday financial organization you then need to be prepared to have a co-signer organized that will help you. The creditors will possibly base their very personal selection the usage of a few factors however the best might be your credit score rating ratings. There are a few loan investors if you want to furthermore take a look at your artwork history and make a preference based totally totally on that but in almost all instances it's going to rely on your ratings.

메이저놀이터순위 说:
2024年5月26日 14:09

Well, the item is in fact the best on this worthw hile topic. I harmonise with your conclusions and will also virtually thirstily look forward to your drawing close updates. Just saying thank you will simply no longer certainly simply be sufficient, for the large lucidity for your writing. I clearly will at once grab your rss feed to live abreast of any kind of updates. Admirable work and plenty fulfillment in your commercial enterprise enterprize!

메이저놀이터 说:
2024年5月26日 14:12

BTW, and I choice we do not drag this too prolonged, but care to remind us genuinely what form of guns have been getting used on Kurds with the useful resource of Saddams military? To the track of hundreds of hundreds of dull Talk about re-written information

사설토토 说:
2024年5月26日 14:12

There are absolutely lots of info like that to reflect onconsideration on. That is a wonderful factor to supply up. I offer the thoughts above as regular suggestion however truly there are questions much like the only you convey up in which the maximum vital thing will probably be going for walks in straightforward proper religion. I don?T recognize if tremendous practices have emerged round things like that, however I’m excessive excellent that your process is in reality recognized as an excellent recreation. Each women and boys experience the impact of simplest a 2d’s pride, for the relaxation of their lives.

메이저놀이터 说:
2024年5月26日 14:13

This is actually the kind of information I have been trying to find. Thank you for writing this information

메이저사이트 说:
2024年5月26日 14:13

Thank you so much as you have been willing to share information as ABC.

먹튀검증 说:
2024年5月26日 14:14

Nice to be visiting your blog again, it has been months for me. Well this article that i've been waited for so long. I need this article to complete my assignment in the college, and it has the same topic with your article. Thanks, great share 

메이저사이트순위 说:
2024年5月26日 14:15

Thanks for every other informative site. The place else may just I get that kind of information written in such an ideal means? I have a venture that I’m just now operating on, and I have been on the look out for such information. 

카지노쿠폰 说:
2024年5月26日 14:15

Firstly what’s an internet net page? Actually a net web site is called a type of internet based totally magazine of which anyone can check out using the planet large net. Weblogs are up to date often and consequently the people that create and replace blogs are called bloggers. Blogging can be defined as term used by a blogger while growing content articles for a running a blog internet web page and weblogs are typically approximately any hassle or subject matter.

메이저토토사이트 说:
2024年5月26日 15:45

This is actually the kind of information I have been trying to find. Thank you for writing this information

คาสิโน 说:
2024年5月26日 15:46

Last but not least, understand that cryptocurrency arbitrage is not really a guaranteed in full road to riches. It requires determination, continuous understanding, and a willingness to adapt. Show patience, keep disciplined, and always prioritize the protection of one's assets.

토토사이트검증 说:
2024年5月26日 15:47

Howdy simply wanted to provide you a brief heads up. The terms on your content material appear to be on foot off the display in Safari. I’m not positive if that could be a format hassle or some component to do with browser compatibility however I figured I’d located as a lot as will can help you realise. The format appearance super even though! Hope you get the problem resolved fast. Kudos

모두의토토 说:
2024年5月26日 15:50

Undeniably imagine that that you stated. Your favorite reason appeared to be at the internet the simplest thing to be aware of. I say to you, I definitely get irked whilst people consider concerns that they plainly don’t recognise about. You controlled to hit the nail upon the top and outlined out the whole thing with no need side-effects , people can take a signal. Will probably be back to get more. Thanks!

토토사이트 说:
2024年5月26日 16:31

I exactly wanted to realize you all all once more. I do not recognize what I ought to have determined within the absence of the shape of strategies revealed by way of way of you at once on that industry. It had been a hard condition for my part, but , locating out this specialised manner you dealt with the trouble compelled me to leap for pride. I’m thankful in your assist and further pray you discover what a first-rate procedure you are constantly putting in coaching women and men through a weblog. Most possibly you’ve in no way come across everyone.

먹튀검증사이트 说:
2024年5月26日 16:32

Undeniably imagine that that you stated. Your favorite reason appeared to be at the internet the simplest thing to be aware of. I say to you, I definitely get irked whilst people consider concerns that they plainly don’t recognise about. You controlled to hit the nail upon the top and outlined out the whole thing with no need side-effects , people can take a signal. Will probably be back to get more. Thanks!

안전놀이터 说:
2024年5月26日 16:33

The Gems Bonanza slot from the online slot site takes players into the sparkling world of shining diamonds and precious stones. Its richly colored graphic design and smooth animations create an exciting and engaging atmosphere. The game's reels are decorated with a variety of precious stones, including emeralds, sapphires, topaz, rubies, and amethysts. The beautiful background with bright colors adds a luxurious feel to this game.

안전토토사이트 说:
2024年5月26日 16:33

Nothing on this planet is while important being a parent when you have the effect of the lifestyle of another man. For your kids to have great results, you ought to make certain he has the many tools to achieve this entire world. Remember, the globe can always be cruel for you to anyone, the two adults along with children.

토토24 说:
2024年5月26日 16:34

Nothing on this planet is while important being a parent when you have the effect of the lifestyle of another man. For your kids to have great results, you ought to make certain he has the many tools to achieve this entire world. Remember, the globe can always be cruel for you to anyone, the two adults along with children.

안전한카지노사이트 说:
2024年5月26日 16:35

Nothing on this planet is while important being a parent when you have the effect of the lifestyle of another man. For your kids to have great results, you ought to make certain he has the many tools to achieve this entire world. Remember, the globe can always be cruel for you to anyone, the two adults along with children.

먹튀검증업체 说:
2024年5月26日 16:36

Well, the item is in fact the best on this worthw hile topic. I harmonise with your conclusions and will also virtually thirstily look forward to your drawing close updates. Just saying thank you will simply no longer certainly simply be sufficient, for the large lucidity for your writing. I clearly will at once grab your rss feed to live abreast of any kind of updates. Admirable work and plenty fulfillment in your commercial enterprise enterprize!

메이저사이트순위 说:
2024年5月26日 16:38

After reading a variety of your blogposts I staleness say i launch this fact one to normally be great. I soul a weblog additionally and require to repost some shears of your articles by myself diary net site. Should or not it's okay if I use this as everlasting I very very own testimonial your net blog or create a inward unification to your article I procured the snipping from? If now not I recognize and couldn't do it at the same time as now not having your tolerance . I change accumulation scarred this article to sound and zynga calculate supposed for statement. Anyway realize it either way!

먹튀검증업체 说:
2024年5月26日 16:38

White wine is exceedingly regarded for purchasing very sensual and classy alcoholic drink. It is especially available in yellow or golden color. White wine is extensively liked all throughout the continents due to its scrumptious flavour. One can anytime pair white wine with chicken so that you can produce a mild meal looks scrumptious. But, to assist all you starters for higher wine merging we’ve prepared this white wine chef for starters.


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter

Host by is-Programmer.com | Power by Chito 1.3.3 beta | Theme: Aeros 2.0 by TheBuckmaker.com