[hdu4453]looploop [treap/splay]

題意:給定一個循環序列,支持以下操作:區間增加,區間翻轉,單點插入/刪除,移動光標,詢問光標所指的位置的值。

首先一眼看出可以用線性數據結構(平衡樹)來維護。然後這些操作都很基礎。但是要注意光標怎麼處理。可以維護一個k記錄它指向第幾個,但是這樣在區間操作時候可能區間分別位於這個序列的兩端(因爲你破環爲鏈了)。比較好的方式是每次人工使得光標指向的元素位於序列的最左端。

這種題以前都是寫的splay,我之前也比較依賴這種數據結構。但是這種題目需要大量的刪除、插入、拼接操作,splay由於維護了父親指針,在做這種操作的時候需要修改的指針特別多。前幾天專門學了非旋轉式treap,由於這種treap的一切操作是基於split/merge的,所以幾乎不用手動修改指針,所有指針的修改可以依賴這兩個操作來完成。

理論上treap的常數應該接近avl和sbt這類自平衡二叉樹,和splay完全不是一個數量級的,因爲splay在旋轉過程中需要修改大量的指針。但這道題上非選旋轉式treap好像實際運行中優越性並不是很強。我猜想是這題數據範圍比較小,指針修改比較少的原因,因爲兩個都只跑了400+ms。

這種treap的建樹挺有意思的。隨機化程度簡直高仿。不過不知道一開始就像splay那樣建成個滿二叉樹可不可以?這樣需要手動給節點賦從下往上遞增的僞隨機值。

注意newnode的時候新建節點size爲1!!!坑了我20多分鐘!!!


#include<iostream>
#include<cstdio>
#include<cstring>
#include<utility>
#include<algorithm>
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define erp(i,a,b) for(int i=a;i>=b;--i)
#define LL long long
#define fi first
#define se second
using namespace std;
typedef pair<int, int> pii;
const int MAXN = 200005;

inline int ran()
{
	static int sd = 1237;
	return sd = (sd*12371237)&0x7fffffff;
}

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

int N, M, k1, k2;

struct Treap
{
	int lch[MAXN], rch[MAXN], sz[MAXN], fix[MAXN];
	int val[MAXN], dt[MAXN];
	bool rev[MAXN];
	int ncnt, root;

	void clear()
	{
		ncnt = root = 0;
		fix[0] = -1;
		sz[0] = val[0] = dt[0] = 0;
		lch[0] = rch[0] = 0;
	}

	inline void uprev(int x)
	{
		if (x) swap(lch[x], rch[x]), rev[x] ^= 1;
	}

	inline void upadd(int x, int d)
	{
		if (x) val[x] += d, dt[x] += d;
	}

	inline void pushdown(int x)
	{
		if (!x) return;
		if (rev[x])
		{
			uprev(lch[x]), uprev(rch[x]);
			rev[x] = 0;
		}
		if (dt[x])
		{
			upadd(lch[x], dt[x]);
			upadd(rch[x], dt[x]);
			dt[x] = 0;
		}
	}

	inline void pushup(int x)
	{
		sz[x] = sz[lch[x]] + sz[rch[x]] + 1;
	}

	int NewNode(int i = 0)
	{
		++ncnt;
		lch[ncnt] = rch[ncnt] = dt[ncnt] = 0;
		fix[ncnt] = ran();
		val[ncnt] = i;
		rev[ncnt] = 0;
		sz[ncnt] = 1;
		return ncnt;
	}

	int merge(int a, int b) //大根堆
	{
		if (!a || !b) return a|b;
		pushdown(a);
		pushdown(b);
		if (fix[a] > fix[b])
		{
			rch[a] = merge(rch[a], b);
			return pushup(a), a;
		}
		else
		{
			lch[b] = merge(a, lch[b]);
			return pushup(b), b;
		}
	}

	pii split(int x, int k)
	{
		if (!x) return pii(0, 0);
		pushdown(x);
		pii y;
		if (sz[lch[x]] >= k)
		{
			y = split(lch[x], k);
			lch[x] = y.se, y.se = x;
		}
		else
		{
			y = split(rch[x], k - sz[lch[x]] - 1);
			rch[x] = y.fi, y.fi = x;
		}
		return pushup(x), y;
	}

	void del(int k)
	{
		pii x = split(root, k-1);
		pii y = split(x.se, 1);
		root = merge(x.fi, y.se);
	}

	int sta[MAXN];
	int build(int *a, int n)
	{
		int x, las, p = 0;
		rep(i, 1, n)
		{
			x = NewNode(a[i]);
			las = 0;
			while (p && fix[sta[p]] < fix[x])
				pushup(las = sta[p]), sta[p--] = 0;
			if (p) rch[sta[p]] = x;
			lch[x] = las;
			sta[++p] = x;
		}
		while (p) pushup(sta[p--]);
		return sta[1];
	}

	void init(int *a, int n)
	{
		clear();
		root = build(a, n);
	}

	void add(int d)
	{
		pii x = split(root, k2);
		upadd(x.fi, d);
		root = merge(x.fi, x.se);
	}

	void reverse()
	{
		pii x = split(root, k1);
		uprev(x.fi);
		root = merge(x.fi, x.se);
	}

	void ins(int t)
	{
		pii x = split(root, 1);
		int tmp = NewNode(t);
		root = merge(merge(x.fi, tmp), x.se);
	}

	void del()
	{
		root = split(root, 1).se;
	}

	void move(int dir)
	{
		int tot = sz[root];
		pii x = split(root, dir==1 ? tot-1 : 1);
		root = merge(x.se, x.fi);
	}

	int quary()
	{
		pii x = split(root, 1);
		int ans = val[x.fi];
		root = merge(x.fi, x.se);
		return ans;
	}
} tp;

int arr[MAXN];

int main()
{
	int cid = 0, x;
	char op[99];
	while (~scanf("%d%d%d%d", &N, &M, &k1, &k2))
	{
		if (!(N|M|k1|k2)) break;
		printf("Case #%d:\n", ++cid);
		rep(i, 1, N) get(arr[i]);
		tp.init(arr, N);
		while (M --)
		{
			scanf("%s", op);
			switch(op[0])
			{
				case 'a': get(x); tp.add(x); break;
				case 'r': tp.reverse(); break;
				case 'i': get(x); tp.ins(x); break;
				case 'd': tp.del(); break;
				case 'm': get(x); tp.move(x); break;
				default  : printf("%d\n", tp.quary());
			}
		}
	}
	return 0;
}


發佈了98 篇原創文章 · 獲贊 26 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章