sgu187:Twist and whirl -- want to cheat(splay+区间翻转)

题意:

裸的区间翻转。

分析:

我们可以ws的用一下stl的reverse,如果手写的话splay较好。

类似线段树lazy思想,我们找到l-1项和r+1项,将其分别splay到root和右孩子,之间夹着的区间我们给其一个翻转。

问题①:找到当前序列中的第k项?

我们需要对每一个结点记录以该结点为根的子树的结点数size,从root出发向做左或向右找。

我们需要在rotate的时候更新size。

问题②:如何翻转?

找到l-1和r+1后,将其夹着的区间的根结点的rev值^1即可。

问题③:何时rev起作用?

Ⅰrotate之前将其传给子结点;

Ⅱ找第k项结点时,每往下走一步时,都要将其传给子结点。

其他的问题应该可以随意脑补了。

#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 130009;
int n, m;
int f[MAXN], c[MAXN][2];
int root;
int size[MAXN];
bool rev[MAXN];
int maketree(int l, int r, int fa)
{
	int mid = (l+r)>>1;
	f[mid] = fa;
	if(l <= mid-1) c[mid][0] = maketree(l, mid-1, mid);
	if(mid+1 <= r) c[mid][1] = maketree(mid+1, r, mid);	
	size[mid] = size[c[mid][0]]+size[c[mid][1]]+1;
	return mid;
}

void down(int p)
{
	if(!rev[p]) return ;
	rev[p] = 0;
	swap(c[p][0], c[p][1]);
	rev[c[p][0]] ^= 1;
	rev[c[p][1]] ^= 1;	
}

void rotate(int s, int &root)
{
	down(s);
	int x = f[s], y = f[x];
	int p = (s==c[x][1]), q = p^1;
	int fp = (x==c[y][1]);
	if(x == root) root = s;
	else c[y][fp] = s;
	size[s] = size[x];size[x] = size[c[s][q]]+size[c[x][q]]+1;
	f[s] = y;f[x] = s;f[c[s][q]] = x;
	c[x][p] = c[s][q];c[s][q] = x;
}

void splay(int p, int &root)
{
	while(p != root)
	{
		int x = f[p], y = f[x];
		if(x != root)
		{
			if((c[x][0] == p)==(c[y][0] == x)) rotate(x, root);
			else rotate(p, root);	
		}
		rotate(p, root);
	}
}

int find(int p)
{
	int now = root;
	while(1)
	{
		down(now);
		if(p == size[c[now][0]]+1) break;	
		if(p > size[c[now][0]]) p -= size[c[now][0]]+1, now = c[now][1];
		else now = c[now][0];
	}
	return now;
}

int main()
{
	scanf("%d%d", &n, &m);
	root = (1+n+2)>>1;
	maketree(1, n+2, 0);
	for(int i = 1, l, r; i <= m; ++i)
	{
		scanf("%d%d", &l, &r);
		int x = find(l), y = find(r+2);
		splay(x, root);
		splay(y, c[root][1]);
		rev[c[y][0]] ^= 1;
	}
	for(int i = 2; i <= n+1; ++i)
		printf("%d ", find(i)-1);
	return 0;	
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值