zoukankan      html  css  js  c++  java
  • 0x40数据结构进阶(0x42 树状数组)例题3:谜一样的牛

    题意

    题目链接

    【题意】
     有n头奶牛,已知它们的身高为 1~n 且各不相同,但不知道每头奶牛的具体身高。
     现在这n头奶牛站成一列,已知第i头牛前面有Ai头牛比它低,求每头奶牛的身高。
    
     【输入格式】
     第1行:输入整数n。
     第2..n行:每行输入一个整数Ai,第i行表示第i头牛前面有Ai头牛比它低。
     (注意:因为第1头牛前面没有牛,所以并没有将它列出)
    
     【输出格式】
     输出包含n行,每行输出一个整数表示牛的身高。
     第i行输出第i头牛的身高。
    
     【数据范围】
    1≤n≤105
    
    【输入样例】
    5
     1
     2
     1
     0
    【输出样例】
    2
     4
     5
     3
     1
    

    题解

    方法1

    当时并没有看到树状数组QAQ,就直接用平衡树了。

    我们一开始设第一个数字为(1)

    然后对于第(i)个数字,我们把前面值域为([a[i],i-1])的数字全部加(1),同时自己等于(a[i]),那么就可以完成这个序列的构建,而这个操作我们可以用平衡树随便解决。

    时间复杂度(O(nlogn))

    不足:常数大。

    //FHQ treap
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #define  N  110000
    using  namespace  std;
    int  siz[N],vio[N],key[N],son[N][2],cnt,n,root,lazy[N];
    int  a[N];
    inline  void  update(int  x){siz[x]=siz[son[x][0]]+siz[son[x][1]];}
    inline  void  pushdown(int  x){key[x]+=lazy[x];lazy[son[x][0]]+=lazy[x];lazy[son[x][1]]+=lazy[x];lazy[x]=0;}
    void  spilt(int  now,int  k,int  &x,int  &y)
    {
        if(!now)x=0,y=0;
        else
        {
            pushdown(now);
            if(key[now]<=k)x=now,spilt(son[x][1],k,son[x][1],y);
            else  y=now,spilt(son[y][0],k,x,son[y][0]);
            update(x);update(y);
        }
    }
    int  merge(int  A,int  B)
    {
        if(!A  ||  !B)return  A+B;
        pushdown(A);pushdown(B);
        if(vio[A]<=vio[B])son[A][1]=merge(son[A][1],B);
        else  son[B][0]=merge(A,son[B][0]),A^=B^=A^=B;
        update(A);return  A;
    }
    void  add(int  x)
    {
        cnt++;siz[cnt]=1;vio[cnt]=rand();key[cnt]=x;
        if(!(cnt^1))root=1;
        else
        {
            int  x1,x2;spilt(root,x,x1,x2);
            x1=merge(x1,cnt);root=merge(x1,x2);
        }
    }
    void  jia(int  l,int  r,int  k)
    {
        if(l<=r)
        {
            int  x,y,z;spilt(root,l-1,x,y);spilt(y,r,y,z);
            lazy[y]+=k;
            root=merge(x,y);root=merge(root,z);
        }
    }
    void  dfs(int  x)
    {
        if(!x)return  ;
        pushdown(x);
        dfs(son[x][0]);dfs(son[x][1]);
    }
    int  main()
    {
        srand(999);
        scanf("%d",&n);
        for(int  i=2;i<=n;i++)scanf("%d",&a[i]);
        add(1);
        for(int  i=2;i<=n;i++)
        {
            jia(a[i]+1,i-1,1);
            add(a[i]+1);
        }
        dfs(root);
        for(int  i=1;i<=n;i++)printf("%d
    ",key[i]);
        return  0;
    }
    

    方法2

    后来学习了一下,知道了树状数组or权值线段树的做法。

    就是维护一个长度为(n)(01)序列,第(i)位表示是否这个数字被用过。

    很明显,(H_n=a_n+1)然后删掉(H_{n}),即01序列中的(H_{n})位变成(0)

    那么(H_{n-1}=?),很明显,既然他前面有(a_{n-1})个数字小于他,那么(H_{n-1})就是除(H_{n})以外第(a_{n-1}+1)大的数字,而这个可以在权值线段树上搞(O(nlogn)),当然也可以树状数组+二分(O(nlog^2n)),或者树状数组加倍增(O(nlogn))(其实相当于在线段树上跳,码量小)。

    那么对于(H_i),就是除(H_{i+1})~(H_{n})中第(a_{i}+1)小的数字,每次找到一个(H_{i})在01序列删掉这个数字,然后维护一下就行了。

    无代码QMQ 。

  • 相关阅读:
    Delphi播放铃声
    小技巧
    Delphi线程中使用waitfor返回值
    window安装、启动consul
    kali2020-bash: openvas-setup:未找到命令 ,解决办法
    zookeeper 客户端
    redis 集群
    activeMQ
    Shiro
    Eclipse Java注释模板设置详解
  • 原文地址:https://www.cnblogs.com/zhangjianjunab/p/11735205.html
Copyright © 2011-2022 走看看