码迷,mamicode.com
首页 > 其他好文 > 详细

codevs 1743 反转卡片 rope or splay

时间:2017-12-20 03:54:56      阅读:199      评论:0      收藏:0      [点我收藏+]

标签:splay   技术分享   需要   简单的   ==   alt   code   substr   read   

【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

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

 

codevs 1743 反转卡片 rope or splay

标签:splay   技术分享   需要   简单的   ==   alt   code   substr   read   

原文地址:http://www.cnblogs.com/fengzhiyuan/p/8067628.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!