Shuffle Cards (splay)

题目大意:给定一个数组1 2 3 4 5…
有m次操作,每次给定一个p和s,然后将数组中[p,p+s-1]中的数放到数组最前方。最后输出操作完后的顺序
实际上这个操作转化成可以分成三次翻转
假设p=2,s=3,
1:翻转[1,p+s-1]
4 3 2 1 5
2:翻转[1,3]
2 3 4 1 5
3 翻转[4,4]
2 3 4 1 5
形式化来讲就是

int l = 1, r = p + s - 1; 
		rev(l, r); //
		rev(l, s);
		rev(s + 1, r);

显然,这是经典的splay翻转板子,抄一份模板即可

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
const int N = 1e6 + 10;

struct node {
    
    
	int s[2], fa, id, siz;
	int flag;
	void init(int _id, int _fa) {
    
    
		id = _id;
		fa = _fa;
	}
} tr[N];

int n, m, idx, root;




void push_up(int u) {
    
    
	tr[u].siz = tr[tr[u].s[0]].siz + tr[tr[u].s[1]].siz + 1;
}

void push_down(int u) {
    
    
	swap(tr[u].s[0], tr[u].s[1]);
	tr[tr[u].s[0]].flag ^= 1;
	tr[tr[u].s[1]].flag ^= 1;
	tr[u].flag = 0;
}


//把x点旋转到k点
void rotate(int x) {
    
    
	int y = tr[x].fa, z = tr[y].fa;//找到父亲与祖父
	int k = tr[y].s[1] == x; //判断x是否为父亲的右儿子
	tr[z].s[tr[z].s[1] == y] = x, tr[x].fa = z;
	tr[y].s[k] = tr[x].s[k ^ 1], tr[tr[x].s[k ^ 1]].fa = y;
	tr[x].s[k ^ 1] = y;
	tr[y].fa = x;//反正就是把x转上去一位,不需要记过程
	push_up(y);
	push_up(x);

}


void splay(int x, int k) {
    
    
	while (tr[x].fa != k) {
    
    
		int y = tr[x].fa, z = tr[y].fa; //分别是y父亲与z祖父
		if (z != k) {
    
    
			if ((tr[y].s[1] == x) != (tr[z].s[1] == y))
				rotate(x);//把x往上旋转一位
			//是父亲的右儿子是x,且祖父的右儿子是y(要求两者一左一右才能转x)

			//如果都是左左或者右右
			else
				rotate(y);//把y往上旋转一位
		}
		rotate(x);//再旋转一位x
	}
	if (!k)
		root = x;
}

void insert(int v) {
    
    
	int u = root, p = 0;
	while (u) {
    
    
		p = u;
		u = tr[u].s[v > tr[u].id]; //如果插入值比当前节点大就插入到右儿子
	}
	u = ++idx; //分配节点编号
	if (p)
		tr[p].s[v > tr[u].id] = u;
	tr[u].init(v, p);
	splay(u, 0); //把u节点转到0点上
}

int get(int u, int k) {
    
    
	if (tr[u].flag)
		push_down(u);
	int cnt = tr[tr[u].s[0]].siz;
	if (cnt >= k)
		return get(tr[u].s[0], k);
	else if (cnt + 1 == k)
		return u;
	else {
    
    
		k -= cnt + 1;
		return get(tr[u].s[1], k);
	}
	return -1;
}

void rev(int l, int r) {
    
    
	l = get(root, l), r = get(root, r + 2);
	splay(l, 0); splay(r, l);//把两个区间转上去,此时l,r之间的区域就出现在了r的左儿子的位置上
	tr[tr[r].s[0]].flag ^= 1; //把l到r区间打上旋转标记
}

void print(int u) {
    
    
	if (tr[u].flag)
		push_down(u);//遇到标记先处理
	if (tr[u].s[0])
		print(tr[u].s[0]);
	if (1 <= tr[u].id && tr[u].id <= n)
		cout << tr[u].id << " ";
	if (tr[u].s[1])
		print(tr[u].s[1]);
}

void solve() {
    
    
	cin >> n >> m;
	for (int i = 0; i <= n + 1; i++) {
    
    
		insert(i);
	}
	while (m--) {
    
    
		int p, s;
		cin >> p >> s;
		int l = 1, r = p + s - 1; //r是取出时的右端
		rev(l, r); //
		rev(l, s);
		rev(s + 1, r);

	}
	print(root);

}




signed main() {
    
    
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);

	solve();
}




おすすめ

転載: blog.csdn.net/fdxgcw/article/details/121290319