ZJOI 2006 书架

题意

请你维护一个长度为\(n\)的排列,要求支持以下操作

  • 把权值为\(v\)的元素放到序列的首端
  • 把权值为\(v\)的元素放到序列的尾端
  • 把权值为\(v\)的元素和它前一个或后一个元素进行交换
  • 询问权值为\(v\)的元素之前有多少个元素
  • 询问第\(k\)个元素的权值

解法

可以用无旋Treap来维护区间

和Splay一样,Treap可以用来维护区间是由于它的下标的中序遍历是整个序列的映射

我们建立由权值到位置的映射pos,我们就能直接找到权值为\(v\)的元素在Treap上对应的结点

除了无旋Treap的基础操作外,我们发现由于维护的现在是下标而不是权值,如果要求序列中的第\(k\)个数我们并不能用传统的查询第\(k\)大的方式进行

我们维护每个节点的父亲,对于权值为\(v\)的元素,我们暴力向上跳父亲,统计所有比当前数小的元素个数

这样我们就得到了权值为\(v\)的元素在排列中的位置

注意,由于是维护序列,这里的Split操作是按照树的大小进行分裂的,若我们对整颗树进行参数为\(k\)的Split操作,这时候我们得到的大小为\(k\)的子树即为排列的前\(k\)个数


代码

#include <ctime>
#include <cstdio>
#include <cstdlib>

using namespace std;

const int N = 1e5 + 10;

int read();

int n, m;
int pos[N];

char op[10];

struct FHQ_Treap {
#define ls(x) t[x].ch[0]
#define rs(x) t[x].ch[1]
#define fa(x) t[x].fa
	
	int cnt;
	int root, a, b, c, d;	
		
	struct node {
		int siz, val, rnd, fa;
		int ch[2];
		node() { ch[0] = ch[1] = 0; }		
	} t[N];
	
	FHQ_Treap() : cnt(0), root(0) {}
	
	int newnode(int v) {
		++cnt;
		t[cnt].siz = 1, t[cnt].val = v, t[cnt].rnd = rand() << 15 | rand();
		return cnt;
	}
	
	void update(int x) {
		t[x].siz = t[ls(x)].siz + t[rs(x)].siz + 1;
		fa(ls(x)) = fa(rs(x)) = x;
	}
	
	void split(int x, int k, int& lt, int& rt) {
		if (!x)
			return lt = rt = 0, void();
		if (k <= t[ls(x)].siz)
			rt = x, split(ls(x), k, lt, ls(x));
		else
			lt = x, split(rs(x), k - t[ls(x)].siz - 1, rs(x), rt);
		update(x);
	}
	
	int merge(int x, int y) {
		if (!x || !y)
			return x | y;
		if (t[x].rnd < t[y].rnd) {
			rs(x) = merge(rs(x), y);
			return update(x), x;
		} else {
			ls(y) = merge(x, ls(y));
			return update(y), y;
		}
	}
	
	int kth(int k) {
		split(root, k - 1, a, b);
		split(b, 1, b, c);
		int res = t[b].val;
		root = merge(a, merge(b, c));
		return res;
	}
	
	int find(int x) {
		int res = t[ls(x)].siz + 1;
		for (; x; x = fa(x))
			if (x == rs(fa(x)))  res += t[ls(fa(x))].siz + 1;
		return res;
	}
	
	void insert(int v) {
		root = merge(root, newnode(v));	
	}
	
	void top(int x) {
		split(root, x - 1, a, b);
		split(b, 1, b, c);
		root = merge(b, merge(a, c));
	}
	
	void bottom(int x) {
		split(root, x - 1, a, b);
		split(b, 1, b, c);
		root = merge(merge(a, c), b);	
	}
	
	void change(int x, int t) {
		if (!t)
			return;
		t = (~t) ? 1 : 2;
		split(root, x - t, a, b);
		split(b, 2, b, c);
		split(b, 1, b, d);
		root = merge(a, merge(merge(d, b), c));
	}
	
	void debug(int x) {
		printf("%d ls: %d rs: %d sz: %d\n", x, ls(x), rs(x), t[x].siz);	
		if (ls(x)) debug(ls(x));
		if (rs(x)) debug(rs(x));
	}
	
#undef ls
#undef rs
#undef fa
} tr;

int main() {
	
	srand(time(NULL));
	
	n = read(), m = read();
	
	for (int i = 1; i <= n; ++i) {
		int v = read();
		tr.insert(v);
		pos[v] = i;
	}
	
//	tr.debug(tr.root);
	
	for (int i = 1; i <= m; ++i) {
		
		scanf("%s", op + 1);
		
		int S = read(); 
		int rnk = tr.find(pos[S]);
		
		switch (op[1]) {
		case 'T':
			tr.top(rnk);  
			break;
		case 'B':
			tr.bottom(rnk); 
			break;
		case 'I':
			tr.change(rnk, read()); 
			break;
		case 'A':
			printf("%d\n", rnk - 1);
			break;
		case 'Q':
			printf("%d\n", tr.kth(S));
			break;	
		}
	}
		
	return 0;
}

int read() {
	int x = 0, f = 1, c = getchar();
	while (c < '0' || c > '9')    c == '-' ? f = -1, c = getchar() : c = getchar();
	while (c >= '0' && c <= '9')  x = x * 10 + c - 48, c = getchar();
	return x * f;	
}
posted @ 2019-09-26 19:18  四季夏目天下第一  阅读(90)  评论(1编辑  收藏  举报