Splay复习

CODEVS 1743 翻转卡片

小A将N张卡片整齐地排成一排,其中每张卡片上写了1~N的一个整数,每张卡片上的数各不相同。

比如下图是N=5的一种情况:3 4 2 1 5

接下来你需要按小A的要求反转卡片,使得左数第一张卡片上的数字是1。操作方法:令左数第一张卡片上的数是K,如果K=1则停止操作,否则将左数第1~K张卡片反转。

第一次(K=3)反转后得到:2 4 3 1 5

第二次(K=2)反转后得到:4 2 3 1 5

第三次(K=4)反转后得到:1 3 2 4 5

可见反转3次后,左数第一张卡片上的数变成了1,操作停止。

你的任务是,对于一种排列情况,计算要反转的次数。你可以假设小A不会让你操作超过100000次。

输入描述 Input Description

第1行一个整数N

第2行N个整数,为1~N的一个全排列。

输出描述 Output Description

仅1行,输出一个整数表示要操作的次数。

如果经过有限次操作仍无法满足要求,输出-1。

样例输入 Sample Input

5

3 4 2 1 5

样例输出 Sample Output

3

数据范围及提示 Data Size & Hint

0<N≤300,000。

裸的区间翻转splay,个人感觉splay的精妙之处在于lazy时交换左右儿子,隐性的完成了区间翻转。

在旋转[l,r]时,我们将l-1旋转至根,r+1旋转至根的右儿子。这样根的右儿子的左边各后代就是要旋转的区间了,根据二叉搜素树的性质,把树上的每个点的左右儿子换一换,就完成了旋转。(本来比他大的变小了,比他小的变比他大了),顺便还维护了一下平衡树的时间复杂度O(nlogn)(摊还分析)。

上代码(有很多细节要注意)

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define N 300005
int c[N][2],val[N],pre[N],size[N],rt,a[N],tag[N];
void update(int k)
{
    size[k]=size[c[k][0]]+size[c[k][1]]+1;
}
void build(int l,int r,int fa)
{
    //cout<<"build"<<endl;
    if(l>r) return;
    int mid=(l+r)>>1;
    if(mid<fa) c[fa][0]=mid;else c[fa][1]=mid;
    val[mid]=a[mid],pre[mid]=fa;
    if(l==r)
    {
        size[mid]=1;
        return ;
    }
    build(l,mid-1,mid);build(mid+1,r,mid);
    //size[mid]=size[c[mid][0]]+size[c[mid][1]];
    update(mid);
}

void rotate(int x,int& k)
{
    //cout<<"rotate"<<x<<‘ ‘<<k<<endl;
    int y=pre[x],z=pre[y],l,r;
    if(c[y][0]==x)  l=0,r=1;else  l=1,r=0;
    if(y==k) k=x;else
    {
        if(c[z][0]==y) c[z][0]=x;else c[z][1]=x;
    }
    pre[x]=z;pre[y]=x;
    pre[c[x][r]]=y;
    c[y][l]=c[x][r];
    c[x][r]=y;
    update(y);update(x);
}
void splay(int x,int &k)
{
    //cout<<"splay"<<endl;
    while(x!=k)
    {
        int y=pre[x],z=pre[y];
        if(y!=k)
        {
            if(c[y][0]==x^c[z][0]==y)
            {
                rotate(x,k);
            }else
            {
                rotate(y,k);
                }
        }
        rotate(x,k);
    }
}
void push_down(int k)
{
    tag[k]=0;
    swap(c[k][0],c[k][1]);
    tag[c[k][0]]^=1;tag[c[k][1]]^=1;
}
int find(int k,int rk)
{
    if(tag[k]) push_down(k);
    if(size[c[k][0]]+1==rk) return k;
    if(size[c[k][0]]>=rk) return find(c[k][0],rk);else
    return find(c[k][1],rk-size[c[k][0]]-1);
}
void rever(int l,int r)
{

    int x=find(rt,l);int y=find(rt,r+2);
    //cout<<"rever";
    //cout<<x<<‘ ‘<<y<<endl;
    splay(x,rt);splay(y,c[rt][1]);
    tag[c[y][0]]^=1;
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    scanf("%d",&a[i+1]);
    build(1,n+2,0);
    rt=(n+3)>>1;
    int ans=0;
    while(val[find(rt,2)]!=1)
    {
        ans++;
        rever(1,val[find(rt,2)]);
        if(ans>100000)
        {
            cout<<-1<<endl;
            return 0;
        }
    }
    cout<<ans<<endl;
}
时间: 2024-12-11 10:53:07

Splay复习的相关文章

splay复习小记

简介 splay的原名是伸展树,一种超级实用的数据结构,能快速地干很多数据结构不能干的事情. 很久以前就听说过并且略微地学了一些,但是当时了解地并不是很多. 有些人把splay达成spaly叫做死吧你,(⊙﹏⊙)b 结构 实质上他是一个二叉搜索树,就是每个节点的左儿子在原序列中是在自己左边的,右儿子在原序列中是在自己右边的,构图的方式有很多. 每一个节点都可以存储一些值,表示它的子树中的信息(比如说什么最大值啊,和啊之内的). 构图 fo(i,1,n){ scanf("%d",&

Tyvj P1729 文艺平衡树 Splay

题目: http://tyvj.cn/p/1729 P1729 文艺平衡树 时间: 1000ms / 空间: 131072KiB / Java类名: Main 背景 此为平衡树系列第二道:文艺平衡树 描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1 输入格式 第一行为n,m n表示初始序列有n个数,这个序列依次是(1,2……n-1,n)  m表示翻转操作次数

splay专题复习——bzoj 3224 &amp;amp; 1862 &amp;amp; 1503 题解

[前言]快要省选二试了.上次去被虐出翔了~~这次即便是打酱油.也要打出风採!于是暂停新东西的学习.然后開始复习曾经的知识,为骗分做准备.PS:区间翻转的临时跳过,就算学了也来不及巩固了. [BZOJ3224] 3224: Tyvj 1728 普通平衡树 Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 1477  Solved: 570 Description 您须要写一种数据结构(可參考题目标题).来维护一些数,当中须要提供下面操作: 1. 插入

splay专题复习——bzoj 3224 &amp; 1862 &amp; 1503 题解

[前言]快要省选二试了.上次去被虐出翔了~~这次即便是打酱油,也要打出风采!于是暂停新东西的学习,然后开始复习以前的知识,为骗分做准备.PS:区间翻转的暂时跳过,就算学了也来不及巩固了. [BZOJ3224] 3224: Tyvj 1728 普通平衡树 Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 1477  Solved: 570 Description 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作: 1. 插入

[复习]平衡树splay

#include<iostream> #include<cstdio> #include<cstring> #define read(a) a=init() using namespace std; struct node{ long long fa,ch[2],data,size,cnt; }t[10000003]; long long n,root=0,tot=0,lei,thi; inline long long init()//快读 { long long a=

BZOJ_1014_[JSOI2008]_火星人prefix_(Splay+LCP_Hash+二分)

描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1014 给出一个字符串,有修改,插入,以及询问LCP(i,j)的操作. 分析 LCP在白书上面有介绍,\(LCP(i,j)\)表示以第\(i\)位和以第\(j\)位开头的后缀的最长公共前缀. 先考虑没有插入和修改操作的问题.我们可以用基于Hash的LCP算法. 我们给每一个后缀一个Hash值.其中以第\(i\)为开头的后缀的Hash值为\(H[i]=H[i+1]x+s[i]\). 其中\(x\

模板复习【updating】

马上就要noi了--可能滚粗已经稳了--但是还是要复习模板啊 LCT: bzoj2049 1A 7min # include <stdio.h> # include <string.h> # include <iostream> # include <algorithm> // # include <bits/stdc++.h> using namespace std; typedef long long ll; typedef long dou

ACM之路(20)—— Splay初探

由于数据结构上老师讲了AVL树的rotate,然后去学了一下treap和Splay,这些数据结构还真是神奇啊! treap暂时只知道名次树的作用(就是一段动态变化的有序数列,找第K大的元素,用set显然是O(n)的..). 好,正式介绍SplayTree这个神奇的数据结构:暂时的理解是,用于解决一些线段树解决不了的区间问题,比方说区间翻转,区间删除并插入等等(似乎分块也可以解决一些xjbg的区间问题)..然后,Splay还可以解决掉LCT的问题(暂时还不会,,下次继续学习). 然后就愉快地掏出模

bzoj2733: [HNOI2012]永无乡(splay+启发式合并/线段树合并)

这题之前写过线段树合并,今天复习Splay的时候想起这题,打算写一次Splay+启发式合并. 好爽!!! 写了长长的代码(其实也不长),只凭着下午的一点记忆(没背板子...),调了好久好久,过了样例,submit,1A! 哇真的舒服 调试输出懒得删了QwQ #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<queue> #include