ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

0x40数据结构进阶(0x42 树状数组)例题3:谜一样的牛

2019-10-24 22:03:29  阅读:294  来源: 互联网

标签:lazy 进阶 树状 谜一样 son int 数字 例题 头牛


题意

题目链接

【题意】
 有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\n",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 。

标签:lazy,进阶,树状,谜一样,son,int,数字,例题,头牛
来源: https://www.cnblogs.com/zhangjianjunab/p/11735205.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有