星星之火

[HEOI2016/TJOI2016] 排序 解题报告(二分答案/线段树分裂合并+set)

题目链接:

https://www.luogu.org/problemnew/show/P2824

题目描述:

在2016年,佳媛姐姐喜欢上了数字序列。因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题,需要你来帮助他。这个难题是这样子的:给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序,排序分为两种:1:(0,l,r)表示将区间[l,r]的数字升序排序2:(1,l,r)表示将区间[l,r]的数字降序排序。最后询问第q位置上的数字。

题解:

做法一:二分答案

然而我并没有写,口胡一下就是二分最终的那个数字,假设我们二分的值是$val$,那么在序列中大于等于$val$我们看成1,小于$val$的我们看成0,这样就是一个01序列了。每次操作我们就线段树维护0的个数和1的个数然后区间赋值就是了。最终我们看一下第q个位置是0还是1,如果是1就把$val$调大,否则调小。

做法二:线段树分裂合并+set

主要讲讲这个吧

考虑我们一开始对每个元素建一颗动态开点权值线段树,因为是1到n的排列我们都不需要离散化(其实本身是动态开点好像也没有很大的影响)。这样我们就得到了一个个由一个元素构成的区间,在之后的操作中然后我们用set维护一下每个区间的左端点,右端点,这个区间线段树的根节点和这个区间是单调增的还是单调减的。

不管是操作1还是操作2,我们就是把[l,r]的所有元素合并到一棵线段树,唯一麻烦的就是端点l,r可能是在当前的某个区间里面,这样的话我们还需要把这个区间给分裂,再把我们需要的那部分拿出来合并

代码实现是这样的,注意我们还要修改一下set

set<Dat>::iterator it=s.lower_bound((Dat){0,l,0,0});
    if ((*it).l!=l)
    {
        Dat t=*it;s.erase(it);int tn=0;
        if (t.type==0)
        {
            Seg::split(1,n,t.nd,tn,l-t.l);
            s.insert((Dat){t.l,l-1,tn,0});
            s.insert((Dat){l,t.r,t.nd,0});
            //printf("qq%d %d\n",t.l,l-1);
            //printf("qq%d %d\n",l,t.r);
        }
        else 
        {
            Seg::split(1,n,t.nd,tn,t.r-l+1);
            s.insert((Dat){t.l,l-1,t.nd,1});
            s.insert((Dat){l,t.r,tn,1});
        }
    }
    it=s.lower_bound((Dat){0,r,0,0});
    if ((*it).r!=r)
    {
        Dat t=*it;s.erase(it);int tn=0;
        if (t.type==0)
        {
            Seg::split(1,n,t.nd,tn,r-t.l+1);
            //printf("qq%d\n",tn);
            s.insert((Dat){t.l,r,tn,0});
            s.insert((Dat){r+1,t.r,t.nd,0});
        }
        else 
        {
            Seg::split(1,n,t.nd,tn,t.r-r);
            s.insert((Dat){t.l,r,t.nd,1});
            s.insert((Dat){r+1,t.r,tn,1});
        }
    }

it就是我们当前要分裂的区间,(*it).nd就是这个区间的根节点,l,r是我们这次操作的区间。对于Dat这个结构体我们采取右端点优先,左端点次一级的排序。

由于升降序不同,我们要从要分裂的区间的线段树取出来的部分也不同,可能是后面的,也可能是前面的,这就需要分类讨论一下了(不要怕麻烦,懒得话就不要学这种做法)

还学到了一个回收空间内存的做法,具体时间就是用一个栈保存一下可以使用的结点,每次要用结点的时候让一个元素出栈即可,可见getnode函数

#include<algorithm>
#include<cstring>
#include<iostream>
#include<cstdio>
#include<set>
using namespace std;

int n,m;
namespace Seg
{
    const int N=6e5+15;
    int lx[N],rx[N],siz[N];
    int st[N+10];int top;
    void init() {for (int i=N-1;i>=1;i--) st[++top]=i;}
    int getnode() {int t=st[top--];lx[t]=rx[t]=siz[t]=0;return t;}
    void delnode(int x) {st[++top]=x;}
    #define mid ((l+r)>>1)
    void update(int &o,int l,int r,int x)
    {
        if (!o) o=getnode();
        if (l==r) {siz[o]++;return;}
        if (x<=mid) update(lx[o],l,mid,x);
        else update(rx[o],mid+1,r,x);
        siz[o]=siz[lx[o]]+siz[rx[o]];
    }
    void split(int l,int r,int o,int &x,int k)
    {
        if (!k) return;
        if (!x) x=getnode();
        if (l==r) {siz[o]-=k;siz[x]+=k;return;}
        int t=siz[lx[o]];
        if (t>k) split(l,mid,lx[o],lx[x],k);
        if (t==k) lx[x]=lx[o],lx[o]=0;
        if (t<k) 
        {
            lx[x]=lx[o];lx[o]=0;
            split(mid+1,r,rx[o],rx[x],k-t);
        }
        siz[o]=siz[lx[o]]+siz[rx[o]];
        siz[x]=siz[lx[x]]+siz[rx[x]];
    }
    int merge(int x,int y)
    {
        if (!x||!y) return x|y;
        lx[x]=merge(lx[x],lx[y]);
        rx[x]=merge(rx[x],rx[y]);
        siz[x]+=siz[y];
        delnode(y);
        return x;
    }
    int kth(int l,int r,int o,int k)
    {
        if (l==r) return l;
        if (siz[lx[o]]>=k) return kth(l,mid,lx[o],k);
        else return kth(mid+1,r,rx[o],k-siz[lx[o]]);
    }
}
struct Dat
{
    int l,r,nd,type;
};
bool operator <(const Dat &a,const Dat &b) 
{
    return a.r<b.r||(a.r==b.r&&a.l<b.l);
}
set <Dat> s;
inline int read()
{
    char ch=getchar();
    int s=0,f=1;
    while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9') {s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
    return s*f;
}
int split_node(int l,int r)
{
    set<Dat>::iterator it=s.lower_bound((Dat){0,l,0,0});
    if ((*it).l!=l)
    {
        Dat t=*it;s.erase(it);int tn=0;
        if (t.type==0)
        {
            Seg::split(1,n,t.nd,tn,l-t.l);
            s.insert((Dat){t.l,l-1,tn,0});
            s.insert((Dat){l,t.r,t.nd,0});
            //printf("qq%d %d\n",t.l,l-1);
            //printf("qq%d %d\n",l,t.r);
        }
        else 
        {
            Seg::split(1,n,t.nd,tn,t.r-l+1);
            s.insert((Dat){t.l,l-1,t.nd,1});
            s.insert((Dat){l,t.r,tn,1});
        }
    }
    it=s.lower_bound((Dat){0,r,0,0});
    if ((*it).r!=r)
    {
        Dat t=*it;s.erase(it);int tn=0;
        if (t.type==0)
        {
            Seg::split(1,n,t.nd,tn,r-t.l+1);
            //printf("qq%d\n",tn);
            s.insert((Dat){t.l,r,tn,0});
            s.insert((Dat){r+1,t.r,t.nd,0});
        }
        else 
        {
            Seg::split(1,n,t.nd,tn,t.r-r);
            s.insert((Dat){t.l,r,t.nd,1});
            s.insert((Dat){r+1,t.r,tn,1});
        }
    }
    int re=0;
    while (1)
    {
        it=s.lower_bound((Dat){0,l,0,0});
        if (it==s.end()||(*it).l>r) break;
        Dat t=(*it);s.erase(it);
        re=Seg::merge(re,t.nd);
    }
    //printf("%d\n",re);
    return re;
}
int main()
{
    Seg::init();
    n=read();m=read();
    for (int i=1;i<=n;i++) 
    {
        int t=0;
        Seg::update(t,1,n,read());
        //printf("%d\n",t);
        s.insert((Dat){i,i,t,0});
    }
    while (m--)
    {
        int op=read(),l=read(),r=read();
        int t=split_node(l,r);
        s.insert((Dat){l,r,t,op});
    }
    int q=read();
    int t=split_node(q,q);
    //printf("%d\n",t);
    printf("%d\n",Seg::kth(1,n,t,1));
    return 0;
}

 

posted @ 2018-10-12 08:57 星星之火OIer 阅读(...) 评论(...) 编辑 收藏