Luogu P3165 [CQOI2014]排序机械臂

先讲一下和这题一起四倍经验的题:

这题作为一道十分经典的平衡树维护序列的问题,自然是值得一做的了。

写完翻了下题解发现都是写Splay的dalao,少有的暴力FHQ_Treap党还是用指针实现的。

所以这里略微讲解下数组实现的FHQ_Treap好了,感觉写起来比Splay舒服些。

首先我们要抽象化一下题意:给你\(n\)个数,第\(i\)次操作在\([i,n]\)中找到最小值的位置\(p_i\),并翻转\([i,p_i]\)。最后输出所有\(p_i\)的值。

然后我们考虑转化问题(因为貌似FHQ_Treap不能同时支持基于权值split基于排名的分裂)。

所以离散化是必须的,尤其注意这里不能直接对数组排序(因为会有权值相等的点)。

然后我们记一下每个值原来的位置,再考虑对一个基本序列(即初始时为\(1,2,3,\dots,n\))进行翻转。

手动推导一下我们发现其实就是先找出每次操作位置的排名,然后再基本序列上不停翻转区间即可。

由于FHQ_Treap树高期望\(\log\)的特性,所以我们查询排名的时候可以直接暴力从一个点跳到根然后反着算回来。

最后提一下那种以权值为保证堆性质的值的做法是错误的!这样会导致树高不平衡,一旦遇到单调的数据就卡到\(O(n^2)\)了。

CODE

#include<cstdio>
#include<cctype>
#include<algorithm>
#define RI register int
#define CI const int&
#define Tp template <typename T>
using namespace std;
const int N=100005;
struct data
{
    int val,id;
    inline friend bool operator <(const data& A,const data& B)
    {
        return A.val<B.val||(A.val==B.val&&A.id<B.id);
    }
}a[N]; int n,rk;
class FileInputOutput
{
    private:
        static const int S=1<<21;
        #define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++)
        #define pc(ch) (Ftop<S?Fout[Ftop++]=ch:(fwrite(Fout,1,S,stdout),Fout[(Ftop=0)++]=ch))
        char Fin[S],Fout[S],*A,*B; int Ftop,pt[15];
    public:
        Tp inline void read(T& x)
        {
            x=0; char ch; while (!isdigit(ch=tc()));
            while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));
        }
        Tp inline void write(T x)
        {
            if (!x) return (void)(pc('0'),pc(' ')); RI ptop=0;
            while (x) pt[++ptop]=x%10,x/=10; while (ptop) pc(pt[ptop--]+48); pc(' ');
        }
        inline void Fend(void)
        {
            fwrite(Fout,1,Ftop,stdout);
        }
        #undef tc
        #undef pc
}F;
class FHQ_Treap
{
    private:
        struct treap
        {
            int ch[2],size,dat,fa; bool rev;
            inline treap(CI Dat=0,CI Size=0)
            {
                ch[0]=ch[1]=rev=fa=0; dat=Dat; size=Size;
            }
        }node[N]; int tot,rt,seed,stack[N],top;
        #define lc(x) node[x].ch[0]
        #define rc(x) node[x].ch[1]
        #define fa(x) node[x].fa
        inline int rand(void)
        {
            return seed=(int)seed*482711LL%2147483647;
        }
        inline void swap(int& x,int& y)
        {
            int t=x; x=y; y=t;
        }
        inline void rever(CI x)
        {
            swap(lc(x),rc(x)); node[x].rev^=1;
        }
        inline void pushup(CI x)
        {
            node[x].size=node[lc(x)].size+node[rc(x)].size+1; fa(lc(x))=fa(rc(x))=x;
        }
        inline void pushdown(CI x)
        {
            if (node[x].rev) rever(lc(x)),rever(rc(x)),node[x].rev=0;
        }
        inline void merge(int& now,int x,int y)
        {
            if (!x||!y) return (void)(now=x|y); if (node[x].dat>node[y].dat)
            pushdown(x),now=x,merge(rc(now),rc(x),y),pushup(x); else
            pushdown(y),now=y,merge(lc(now),x,lc(y)),pushup(y);
        }
        inline void split(int now,int& x,int& y,int rk)
        {
            if (!now) return (void)(x=y=0); pushdown(now); if (node[lc(now)].size<rk)
            x=now,split(rc(now),rc(x),y,rk-node[lc(now)].size-1); else
            y=now,split(lc(now),x,lc(y),rk); pushup(now);
        }
    public:
        FHQ_Treap() { seed=233; }
        inline void insert(CI val)
        {
            node[++tot]=treap(rand(),1); merge(rt,rt,tot);
        }
        inline void reverse(RI l,RI r)
        {
            int x,y,z; split(rt,x,y,l-1); split(y,y,z,r-l+1);
            rever(y); merge(y,y,z); merge(rt,x,y);
        }
        inline int get_rk(int now)
        {
            stack[top=1]=now; for (int t=now;fa(t);t=fa(t)) stack[++top]=fa(t);
            while (top) pushdown(stack[top--]); int ret=node[lc(now)].size;
            for (;now;now=fa(now)) if (now==rc(fa(now))) ret+=node[lc(fa(now))].size+1;
            return ret+1;
        }
        #undef lc
        #undef rc
}T;
int main()
{
    //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    RI i; for (F.read(n),i=1;i<=n;++i) F.read(a[i].val),a[i].id=i;
    for (sort(a+1,a+n+1),i=1;i<=n;++i) T.insert(i); for (i=1;i<=n;++i)
    rk=T.get_rk(a[i].id),F.write(rk),T.reverse(i,rk); return F.Fend(),0;
}

原文地址:https://www.cnblogs.com/cjjsb/p/10263886.html

时间: 2024-07-31 21:43:11

Luogu P3165 [CQOI2014]排序机械臂的相关文章

P3165 [CQOI2014]排序机械臂

P3165 [CQOI2014]排序机械臂 题目描述 为了把工厂中高低不等的物品按从低到高排好序,工程师发明了一种排序机械臂.它遵循一个简单的排序规则,第一次操作找到摄低的物品的位置P1,并把左起第一个至P1间的物品反序:第二次找到第二低的物品的位置P2,并把左起第二个至P2间的物品反序...最终所有的物品都会被排好序. 上图给出_个示例,第_次操作前,菝低的物品在位置4,于是把第1至4的物品反序:第二次操作前,第二低的物品在位罝6,于是把第2至6的物品反序... 你的任务便是编写一个程序,确定

洛谷P3165 [CQOI2014]排序机械臂

题目描述 为了把工厂中高低不等的物品按从低到高排好序,工程师发明了一种排序机械臂.它遵循一个简单的排序规则,第一次操作找到摄低的物品的位置P1,并把左起第一个至P1间的物品反序:第二次找到第二低的物品的位置P2,并把左起第二个至P2间的物品反序...最终所有的物品都会被排好序. 上图给出_个示例,第_次操作前,菝低的物品在位置4,于是把第1至4的物品反序:第二次操作前,第二低的物品在位罝6,于是把第2至6的物品反序... 你的任务便是编写一个程序,确定一个操作序列,即每次操作前第i低的物品所在位

[CQOI2014]排序机械臂

洛谷P3165 [CQOI2014]排序机械臂 https://www.luogu.org/problem/show?pid=3165 题目描述 为了把工厂中高低不等的物品按从低到高排好序,工程师发明了一种排序机械臂.它遵循一个简单的排序规则,第一次操作找到摄低的物品的位置P1,并把左起第一个至P1间的物品反序:第二次找到第二低的物品的位置P2,并把左起第二个至P2间的物品反序...最终所有的物品都会被排好序. 上图给出_个示例,第_次操作前,菝低的物品在位置4,于是把第1至4的物品反序:第二次

[BZOJ3506] [Cqoi2014] 排序机械臂 (splay)

Description 同OJ1552 Input Output Sample Input Sample Output HINT Source Solution Q:哎不是同一道题吗为什么分两篇博客来写? A:当然是来骗浏览量了233333333,这里还是会放代码的,当然你左转BZOJ1552的题解也行. 1 #include <bits/stdc++.h> 2 using namespace std; 3 struct spaly 4 { 5 int c[2], fa, siz, rev;

[bzoj1552][Cerc2007]robotic sort&amp;&amp;[bzoj3506][Cqoi2014]排序机械臂

非常垃圾的一道平衡树,结果被日了一天.很难受嗷嗷嗷 首先不得不说网上的题解让我这个本来就不熟悉平衡树的彩笔很难受--并不好理解. 还好Sinogi大佬非常的神,一眼就切掉了,而且用更加美妙的解法. 题意在操作时,就是第i次把编号为i-1和编号i的后继分别提到根和根的右儿子,根的右儿子的左子树打上翻转标记. 用外部数组记录原来高度第几大的在平衡树中编号是多少.就可以直接操作了. 注意有相同的高度,离散化时直接按高度第一关键字,编号第二关键字就行了. 还有每次splay要把根到当前节点都pushdo

LibreOJ2241 - 「CQOI2014」排序机械臂

Portal Description 给出一个\(n(n\leq10^5)\)个数的序列\(\{a_n\}\),对该序列进行\(n\)次操作.若在第\(i\)次操作前第\(i\)小的数在\(p_i\)位置,则翻转区间\([i,p_i]\).易知\(n\)次操作后序列会变为升序.求出每一次的\(p_i\). Solution splay. 题里的\(a_i\)是会重复的...所以先离散化一波,相同的数按位置排名.然后根据初始位置建一棵splay,每次将\(i\)旋转到根求左子树大小即可,区间翻转用

【BZOJ3506】排序机械臂(Splay)

[BZOJ3506]排序机械臂(Splay) 题面 神TMBZOJ没有题面,感谢SYC的题面 洛谷的题面也不错 题解 对于每次旋转的物体 显然可以预处理出来 现在只要模拟旋转操作就行了 至于在哪里放标记的问题 我只在第K大放会鬼.. 所以在Splay里面也放了一次(和LCT一样的) 然而我每次都把排到了正确位置的元素直接给删掉了... 所以跑的很慢很慢... #include<iostream> #include<cstdio> #include<cstdlib> #i

【BZOJ】【1552】【Cerc2007】robotic sort / 【3506】【CQOI2014】排序机械臂

Splay 离散化+Splay维护序列…… 好吧主要说一下我做这道题遇到的几个错误点: 1.离散化 2.由于找到的这个数的位置一定是大于等于 i 的,所以其实在把它splay到根以后,i 结点只能splay到它的左儿子,而不是右儿子……而且相应的,代表这个区间的应该是c[c[root][0]][1]呃……就是root的左儿子的右儿子= =整个反了一下……好吧不要在意这些细节,我也不知道为什么我突然要反过来写[捂脸熊] 3.进行了修改操作以后一定要立即splay修改的结点!在这题中就是当找到最小结

BZOJ 3506 CQOI 2014 排序机械臂 Splay

题目大意:给出一个序列,给出一种排序方式,模拟这种排序方式排序,并输出每次找到的节点的位置. 思路:它让你做什么你就做什么,无非就是个Reverse,很简单.注意一下排序的时候权值相等的情况就行了. CODE: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX 100010 #define INF 0x3f3f3f3f using