zoukankan      html  css  js  c++  java
  • codevs 1743 反转卡片

    1743 反转卡片

    http://codevs.cn/problem/1743/

     时间限制: 2 s
     空间限制: 256000 KB
     题目等级 : 大师 Master
     
    题目描述 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。

    splay区间反转

    以卡片在序列中的位置建立完全平衡splay树

    //  错误理解:以卡片的顺序作为节点权值在树中存储,即存储的是1——n的序列,不是卡片上的数字

    正确理解:权值还是卡片上的数字,只不过没有必要在splay中体现出来,splay中节点编号和卡片在序列中的编号保持一致

    这样树的中序遍历就是序列

    splay的时候没有下传标记

    竟然A了,很神奇

    #include<cstdio>
    #include<algorithm>
    #define N 300010
    using namespace std;
    int n,a[N],ch[N][2],fa[N],siz[N],root,ans;
    bool tag[N];
    inline void update(int k)
    {
        siz[k]=siz[ch[k][0]]+siz[ch[k][1]]+1;
    }
    inline void build(int l,int r,int f)
    {
        if(l>r) return;
        int mid=l+r>>1;
        if(mid<f) ch[f][0]=mid;
        else ch[f][1]=mid;
        siz[mid]=1;fa[mid]=f;
        if(l==r) return;
        build(l,mid-1,mid);
        build(mid+1,r,mid);
        update(mid);
    }
    inline void down(int k)
    {
        tag[k]^=1;tag[ch[k][0]]^=1;tag[ch[k][1]]^=1;
        swap(ch[k][0],ch[k][1]);
    }
    inline void rotate(int x,int & goal)
    {
        int y=fa[x],z=fa[y],kind=ch[y][1]==x;
        if(y==goal) goal=x;
        else {ch[z][ch[z][1]==y]=x;}
        fa[y]=x;fa[x]=z;fa[ch[x][kind^1]]=y;
        ch[y][kind]=ch[x][kind^1];ch[x][kind^1]=y;
        update(y);
    }
    inline void splay(int x,int & goal)
    {
        while(x!=goal)
        {
            int y=fa[x],z=fa[y];
            if(y!=goal)
            {
                if(ch[y][1]==x^ch[x][1]==y) rotate(x,goal);
                else rotate(y,goal);
            }
            rotate(x,goal);
            update(x);
        }
    }
    inline int find(int now,int k)
    {
        if(tag[now]) down(now);
        int l=ch[now][0],r=ch[now][1];
        if(k<=siz[l]) return find(l,k);
        if(siz[l]+1==k) return now;
        return find(r,k-siz[l]-1);
    }
    inline void rever(int l,int r)
    {
        int x=find(root,l-1),y=find(root,r+1);
        splay(x,root);splay(y,ch[x][1]);
        tag[ch[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);
        root=n+3>>1;//build中根节点设为l+r=n+3 
        while(1)
        {
            if(ans>100000) {printf("-1");return 0;}
            int b=find(root,2); //查找序列中第一张卡片的编号,
        //因为全体后移,树中卡片顺序作为节点权值,所以相当于查找排名为2的点 
            if(a[b]==1) {printf("%d",ans);return 0;}
            ans++;
            rever(2,2+a[b]-1);//反转第一张到第1+a[b]-1张,树中下标后移一位 
        }
    }

    当初不是很明白的小细节:

    1、为什么rever里不交换左右孩子?

    因为这颗平衡树的父节点与左右孩子没有包含关系,左右孩子相对独立,交不交换对于本节点没有影响

    但如果rever里交换了,在down里有交换,相当于又换了回去,所以不换

    2、为什么down里不update

    因为交换左右孩子,对于父节点相当于交换左右子树,这里只维护siz,不影响,而左右子树内部并没有交换

    3、为什么全体要后移(区间操作都要在首尾加虚拟节点)?

    因为锁定区间[l,r]时,查找的是l-1和r+1

    如果l=1,查找第0张,因为平衡树里存储的顺序,找第0张相当于找排名为0,没有点的siz为0,r=n同理

    一个卡了一个上午的错误:

    define N 300010

    最初赋初值为300001,一直TLE

    因为全体节点后移一位了

    这就是后果:给自己一个警示,这种错误既没有价值又浪费时间

  • 相关阅读:
    DirectX11 With Windows SDK--35 粒子系统
    Xilinx FPGA 的 DNA 加密
    IIC
    蜂鸟E203系列—— SPI 设计
    SPI
    蜂鸟E203系列—— UART 设计
    蜂鸟E203系列——按键中断设计
    蜂鸟E203系列——Windows下运行hello world例程
    蜂鸟E203系列——定时器中断设计
    什么才是定制化IDE的核心价值?
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/6414979.html
Copyright © 2011-2022 走看看