您的位置:首页 > 其它

【codevs1743】 反转卡片

2016-10-08 15:54 295 查看
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;
}


  

  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: