codevs 1743 反转卡片 rope or splay

【codevs1743】反转卡片

题目描述 Description

【dzy493941464|yywyzdzr原创】

小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。

题解:两种方法,rope,十分简单的,c++#include<ext/rope>可持久化平衡树,。

   splay,区间旋转在其实就想到了splay吧,每次lg,常数大一些。

   

   红色代表虚点,黑色代表实点,先翻转成这个样子,然后对于根的右子树的左子树的根打上旋转标记

   即可。

   旋转标记

   

代码注释了不少

rope代码

 1 #include<cstring>
 2 #include<cmath>
 3 #include<iostream>
 4 #include<algorithm>
 5 #include<cstdio>
 6 #include<ext/rope>
 7 #include<ext/hash_map>
 8
 9 #define N 300007
10 using namespace std;
11 using namespace __gnu_cxx;
12 inline int read()
13 {
14     int x=0,f=1;char ch=getchar();
15     while(ch<‘0‘||ch>‘9‘){if (ch==‘-‘)f=-1;ch=getchar();}
16     while(ch>=‘0‘&&ch<=‘9‘){x=(x<<3)+(x<<1)+ch-‘0‘;ch=getchar();}
17     return x*f;
18 }
19
20 int n,ans;
21 int a[N];
22 rope<int>s1,s2,t1,t2;
23
24 void spin(int x)
25 {
26     t1=s1.substr(0,x);
27     t2=s2.substr(n-x,x);//后者是长度
28     s1=t2+s1.substr(x,n-x);
29     s2=s2.substr(0,n-x)+t1;
30 }
31 int main()
32 {
33     n=read();
34     for (int i=1;i<=n;i++)a[i]=read();
35     for (int i=1;i<=n;i++)s1.push_back(a[i]);
36     for (int i=1;i<=n;i++)s2.push_back(a[n-i+1]);
37     while(s1[0]!=1)
38     {
39         spin(s1[0]);
40         ans++;
41         if (ans>100000)
42         {
43             printf("-1\n");
44             return 0;
45         }
46     }
47     printf("%d\n",ans);
48 }

splay代码

  1 #include<cstring>
  2 #include<cmath>
  3 #include<iostream>
  4 #include<algorithm>
  5 #include<cstdio>
  6
  7 #define N 300007
  8 using namespace std;
  9 inline int read()
 10 {
 11     int x=0,f=1;char ch=getchar();
 12     while(ch<‘0‘||ch>‘9‘){if (ch==‘-‘) f=-1;ch=getchar();}
 13     while(ch>=‘0‘&&ch<=‘9‘){x=(x<<1)+(x<<3)+ch-‘0‘;ch=getchar();}
 14     return x*f;
 15 }
 16
 17 int n,ans,rt;
 18 int a[N];
 19 int c[N][2],fa[N],siz[N],val[N];
 20 bool rev[N];
 21
 22 void update(int k)
 23 {
 24     int l=c[k][0],r=c[k][1];
 25     siz[k]=siz[l]+siz[r]+1;
 26 }
 27 void rotate(int x,int &k)
 28 {
 29     int y=fa[x],z=fa[y],l,r;
 30     if (c[y][0]==x)l=0;else l=1;r=l^1;
 31     if (y==k) k=x;
 32     else
 33     {
 34         if (c[z][0]==y) c[z][0]=x;
 35         else c[z][1]=x;
 36     }
 37     fa[x]=z,fa[y]=x,fa[c[x][r]]=y;
 38     c[y][l]=c[x][r],c[x][r]=y;
 39     update(y),update(x);
 40 }
 41 void splay(int x,int &p)
 42 {
 43     while(x!=p)
 44     {
 45         int y=fa[x],z=fa[y];
 46         if (y!=p)
 47         {
 48             if (c[y][0]==x^c[z][0]==y) rotate(x,p);
 49             else rotate(y,p);
 50         }
 51         rotate(x,p);
 52     }
 53 }
 54 void pushdown(int k)
 55 {
 56     int l=c[k][0],r=c[k][1];
 57     rev[k]^=1,rev[l]^=1,rev[r]^=1;
 58     swap(c[k][0],c[k][1]);
 59 }
 60 void build(int l,int r,int p)
 61 {
 62     if (l>r) return;
 63     int mid=(l+r)>>1;
 64     if (mid<p) c[p][0]=mid;
 65     else c[p][1]=mid;
 66     siz[mid]=1,val[mid]=a[mid],fa[mid]=p;
 67     if (l==r) return;
 68     build(l,mid-1,mid),build(mid+1,r,mid);
 69     update(mid);
 70 }
 71 int find(int p,int rk)//寻找第rk的位置。
 72 {
 73     if (rev[p]) pushdown(p);
 74     int l=c[p][0],r=c[p][1];
 75     if (siz[l]+1==rk) return p;
 76     else if (siz[l]>=rk) return find(l,rk);
 77     else return find(r,rk-siz[l]-1);
 78 }
 79 void spin(int l,int r)
 80 {
 81     int x=find(rt,l),y=find(rt,r+2);//因为加了两个虚节点,所以翻转的时候方便,1为虚节点,将2----当前位置+1这一段旋转出来。
 82     splay(x,rt),splay(y,c[x][1]);//这样根的右子树的左子树就是所求区间。
 83     int z=c[y][0];
 84     rev[z]^=1;//在这个节点上打上标记,可以想一下,现在树是什么样子的。
 85 }
 86 int main()
 87 {
 88     n=read();
 89     for (int i=1;i<=n;i++) a[i+1]=read();
 90     build(1,n+2,0),rt=(1+n+2)>>1;//0只是虚的点,没有用的,1和n+2是哨兵,一线段树的形式建立splay先。
 91     while(val[find(rt,2)]!=1)//第二个是第一个数
 92     {
 93         ans++;
 94         spin(1,val[find(rt,2)]);
 95         if (ans>100000)
 96         {
 97             printf("-1\n");
 98             return 0;
 99         }
100     }//操作直到第一个是1。
101     printf("%d\n",ans);
102 }

   发现手写splay快,常数大但是比c++的大部分的stl还是快的,stl没有开O3,O2

   是很慢的,而且可持久化平衡树更强大,就需要更大的代价。

时间: 2024-10-21 18:13:57

codevs 1743 反转卡片 rope or splay的相关文章

codevs 1743 反转卡片

题目描述 Description [dzy493941464|yywyzdzr原创] 小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)反转

CodeVS1743 反转卡片

传送门 splay裸裸哒区间翻转维护,月考考跪装作很开心的样子 直接贴代码. 1 //CodeVS 2 //by Cydiater 3 //2016.9.11 4 #include <iostream> 5 #include <cstring> 6 #include <string> 7 #include <algorithm> 8 #include <queue> 9 #include <map> 10 #include <c

总结-数据结构

数据结构 1. 树状数组 写起来很方便, 用的比较多, 比线段树更实用吧. 虽然原理到现在不太清楚... 往往没有裸树状数组的题目, 往往和其他算法相结合. 单次修改或查询: O(logn) 1. BZOJ-2716-天使玩偶angel-CDQ分治 cdq分治 2. BZOJ-1878-HH的项链-SDOI2009 离线处理, 求种数 3. BZOJ-3289-Mato的文件管理-莫队+树状数组 莫队, 用树状数组统计逆序对 树状数组的弱点是不支持区间最大最小值什么的, 但是利用它可以统计前缀和

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次后,左数第一

计划做题列表

估计是做不完.. emmmm  不用估计,是肯定 马上就滚回去学文化课了.... ....列表全都是数据结构... mdzz 我可能是傻了 1.数学  P1214 [USACO1.4]等差数列 Arithmetic Progressions https://www.luogu.org/problemnew/show/P1214 2.P4243 [JSOI2009]等差数列 线段树 https://www.luogu.org/problemnew/show/P4243 主席树 P3313 [SDO

●Splay的一些题

●个人感觉: 代码长: 函数多: (很套路): (很强的Splay,无愧于"区间王") ●NOI2005维修数列 一个可以当模板学习的题,包含了众多操作(函数): 区间插入,删除,更新,翻转,询问信息以及"回收空间"(名字很刚)等. update()pushdown() rotate() splay() build() find() split() insert() rec() erase() query() rever() modify()还有main() 建议初

POJ 3580 - SuperMemo - [伸展树splay]

题目链接:http://poj.org/problem?id=3580 Your friend, Jackson is invited to a TV show called SuperMemo in which the participant is told to play a memorizing game. At first, the host tells the participant a sequence of numbers, {A1, A2, ... An}. Then the h

伸展树复习

T1 郁闷的出纳员 一个数据结构,支持单点插入.删除几个不一定连续的点.查询k值操作 初做:2017.2.18   time:1268ms    memory:3MB http://www.cnblogs.com/TheRoadToTheGold/p/6412790.html 现在:2017.3.28   time:570ms   memory:3MB 初做时直接套模板,删除分5种情况, 本题实际只需3种情况 这一次的查询用递归写的 int query(int now,int k) { int

【集训第四天&#183;继续刷题】之 lgh怒刚ypj

继续水题,终于完全掌握了伸展树了,好心痛QAQ. 1.codevs1343 蚱蜢 区间最大值查询+单点移动 最大值查询维护一个mx数组就行,单点移动么,先删除在插入 CODE: 1 /* 2 PS: 比较max值时,要写成 mx[x]=max(a[x],max(mx[l],mx[r]));形式 3 且最好把mx[0]赋值为负无穷大 4 5 取max时,注意初值问题 6 7 */ 8 9 #include<bits/stdc++.h> 10 #define N 100005 11 using n