洛谷 P4402 BZOJ1552 / 3506 [Cerc2007]robotic sort 机械排序

时间:2022-05-12 22:10:48

FHQ_Treap 太神辣

蒟蒻初学FHQ_Treap,于是来到了这道略显板子的题目

因为Treap既满足BST的性质,又满足Heap的性质,所以,对于这道题目,我们可以将以往随机出的额外权值转化为每一个位置的高度,这样我们就可以利用小根堆的性质,甚至连\(Split\)操作都可以省略掉,就可以AC这道题目

步骤:
1.首先找到现在的根节点,即当前高度最低的位置

2.根据Treap的性质,我们只需要将其左子树打一个反转的标记,然后将根节点删除,将其左右两个子树合并,那么,新的子节点就是我们接下来需要的第2小高度的位置

3.重复1,2操作,这个题目就完结撒花辣!!

但是高度有可能是相同的,题目中又要求取靠前的那一个,于是我们将所有的数乘上n,再加上i,这样就可以有效避免这一个问题。

愿望总是美好的


[CQOI2014]排序机械臂愉快的过掉了,但另外一道相同的题目却过不了(robotic sort)。然后,有dalao指出,这样的做法,只要数据是单调的,就会被卡成\(O(n^2)\)

于是,通过查阅资料,我们可以使用一种神奇的方法——笛卡尔树优化建树,这样就可以有效避免这个问题。

对于\(Treap\)来说,我们可以将每一个节点的信息简化为一个二元组\((val,rand)\),意义一看便知

我们按照val的顺序进行插入,而在本题中,val即初始位置1~n,所以不用重新排序。

我们可以维护一个单调栈,从而有效解决这个问题。

对于每一个节点,我们将栈中大于当前节点rand值的节点全部弹出,将他们挂在当前节点的左儿子上,然后再把连同当前节点一起的这一棵树挂在弹完所有值之后的栈顶节点的右儿子上,这样就可以构建出一颗相对平衡的Treap。

用通俗的话讲,对于当前节点u,我们只考虑将其放在最右边的这一条链上,如果他比所有节点的rand值都大,那么当然顺理成章的放在最右边,如果没有,我们就考虑同时维护BST与Heap的性质,于是就有了我们上面的做法。

贴代码啦~

//短短70行解决战斗
#include<bits/stdc++.h>
using namespace std;
const long long maxn=5e5+10;
long long inline read()
{
    long long x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
long long son[maxn][2],val[maxn],rnd[maxn],siz[maxn],cnt,rt,tag[maxn];
inline void upd(long long t){siz[t]=siz[son[t][0]]+siz[son[t][1]]+1;}
inline void down(long long x)
{
    if(tag[x])
    {
        swap(son[x][0],son[x][1]);
        if(son[x][0]) tag[son[x][0]]^=1;
        if(son[x][1]) tag[son[x][1]]^=1;
        tag[x]=0;
    }
}
inline long long init(long long x,long long qaq)
{
    ++cnt,val[cnt]=x,siz[cnt]=1,rnd[cnt]=qaq;
    return cnt;
}
inline long long merge(long long x,long long y)
{
    if(!x||!y) return x+y;
    if(rnd[x]<rnd[y])
    {
        down(x),son[x][1]=merge(son[x][1],y),upd(x);
        return x;
    }
    else
    {
        down(y),son[y][0]=merge(x,son[y][0]),upd(y);
        return y;
    }
}
struct cc{
    long long num,id;
}a[maxn];
int main()
{
    srand(time(0));
    long long opt,n,m,l,r,x,y,z;
    n=read();
    stack<int> q;
    for(int i=1;i<=n;++i)
    {
        scanf("%lld",&rnd[i]),rnd[i]=rnd[i]*n+i,val[i]=i,siz[i]=1;
        while((!q.empty())&&rnd[i]<rnd[q.top()])
            son[i][0]=q.top(),q.pop(),upd(son[i][0]);
        if(!q.empty()) son[q.top()][1]=i;
        q.push(i);
    }
    while(!q.empty()) rt=q.top(),upd(q.top()),q.pop();
    for(int i=1;i<=n;++i)
    {
        down(rt);
        printf("%lld ",siz[son[rt][0]]+i);
        l=son[rt][0],r=son[rt][1];
        son[rt][0]=son[rt][1]=0;
        tag[l]^=1;
        rt=merge(l,r);
    }
    printf("\n");
    return 0;
}