【codevs1743】 反转卡片

http://codevs.cn/problem/1743/ (题目链接)

题意

  给出一个序列{a1,a2,a3···},要求维护这样一种操作:将前a1个数反转,若第a1等于1,则停止操作。

Solution

 

  像这种带有反转区间的操作,大概就是splay了。码了一个晚上。。。

  splay一般就是处理区间反转,区间插入,区间删除这三种线段树等数据结构无法处理的操作,splay难写又难调,经常犯一些鬼畜错误,能不写就尽量不写的好。。splay不是二叉搜索树,但它有一个很优秀的性质:树的中序遍历出来的序列就是原始序列。这样我们方便的就可以处理区间上的问题,比如说对于区间[l,r],我们将l-1splay到树根,将r+1splay到树根的右儿子,那么我们要修改的区间就是树根的右儿子的左儿子子树。

  所以对于这道题,我们每次反转时,先将splay两遍,然后再给节点[l,r]打上标记即可。代码模着hzwer写的,感觉很优秀。

代码

// codevs1743
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define inf 2147483640
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;

const int maxn=300010;
int a[maxn],tr[maxn][2],fa[maxn],size[maxn],val[maxn],rev[maxn];
int n,ans,rt;

void rotate(int x,int &k) {
	int y=fa[x],z=fa[y],l,r;
	if (x==tr[y][0]) l=0;else l=1;
	r=l^1;
	if (y==k) k=x;
	else tr[z][tr[z][1]==y]=x;
	fa[y]=x;fa[x]=z;fa[tr[x][r]]=y;
	tr[y][l]=tr[x][r];tr[x][r]=y;
	//注意这里一定先更新y的size,再更新x的size.
	size[y]=size[tr[y][0]]+size[tr[y][1]]+1;
	size[x]=size[tr[x][0]]+size[tr[x][1]]+1;
}
void splay(int x,int &k) {
	while (x!=k) {
		int y=fa[x],z=fa[y];
		if (y!=k) {   //如果x,y,z在一条链(形象)上,那么先旋y,再旋x.听说可以使平衡树更加平衡
			if (tr[z][0]==y ^ tr[y][0]==z) rotate(x,k);   //不在一条链上
			else rotate(y,k);   //在一条链上
		}
		rotate(x,k);
	}
}
void pushdown(int k) {
	int l=tr[k][0],r=tr[k][1];
	rev[k]^=1;rev[l]^=1;rev[r]^=1;   //如果同一个区间被反转2次,那么就等价于不反转.
	swap(tr[k][0],tr[k][1]);
}
int find(int k,int x) {   //在树中寻找序列中第rk个数
	if (rev[k]) pushdown(k);
	int l=tr[k][0],r=tr[k][1];
	if (size[l]+1==x) return k;
	else if (x<=size[l]) return find(l,x);
	else return find(r,x-size[l]-1);
}
void build(int l,int r,int f) {
	if (l>r) return;
	int mid=(l+r)>>1;
	//tr[][]记录当前节点的左儿子与右儿子
	if (mid<f) tr[f][0]=mid;
	else tr[f][1]=mid;
	//size[]记录子树大小,val[]记录数值,fa[]记录父亲.
	rev[mid]=0;val[mid]=a[mid];size[mid]=1;fa[mid]=f;
	if (l==r) return;
	build(l,mid-1,mid);
	build(mid+1,r,mid);
	size[mid]=size[tr[mid][0]]+size[tr[mid][1]]+1;
}
void rever(int l,int r) {
	int x=find(rt,l),y=find(rt,r+2);
	splay(x,rt);splay(y,tr[rt][1]);
	rev[tr[y][0]]^=1;
}
int main() {
	scanf("%d",&n);
	for (int i=1;i<=n;i++) scanf("%d",&a[i+1]);
	build(1,n+2,0);rt=(3+n)>>1;
	int ans=0;
	while (val[find(rt,2)]!=1) {
		ans++;
		rever(1,val[find(rt,2)]);
		if (ans>100000) {printf("-1");return 0;}
	}
	printf("%d",ans);
	return 0;
}

  

  

posted @ 2016-10-08 15:54  MashiroSky  阅读(239)  评论(0编辑  收藏  举报