[HEOI2016/TJOI2016] 排序 解題報告(二分答案/線段樹分裂合併+set)

題目連接:

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

題目描述:

在2016年,佳媛姐姐喜歡上了數字序列。於是他常常研究關於序列的一些奇奇怪怪的問題,如今他在研究一個難題,須要你來幫助他。這個難題是這樣子的:給出一個1到n的全排列,如今對這個全排列序列進行m次局部排序,排序分爲兩種:1:(0,l,r)表示將區間[l,r]的數字升序排序2:(1,l,r)表示將區間[l,r]的數字降序排序。最後詢問第q位置上的數字。ios

題解:

作法一:二分答案

然而我並無寫,口胡一下就是二分最終的那個數字,假設咱們二分的值是$val$,那麼在序列中大於等於$val$咱們當作1,小於$val$的咱們當作0,這樣就是一個01序列了。每次操做咱們就線段樹維護0的個數和1的個數而後區間賦值就是了。最終咱們看一下第q個位置是0仍是1,若是是1就把$val$調大,不然調小。函數

作法二:線段樹分裂合併+set

主要講講這個吧spa

考慮咱們一開始對每一個元素建一顆動態開點權值線段樹,由於是1到n的排列咱們都不須要離散化(其實自己是動態開點好像也沒有很大的影響)。這樣咱們就獲得了一個個由一個元素構成的區間,在以後的操做中而後咱們用set維護一下每一個區間的左端點,右端點,這個區間線段樹的根節點和這個區間是單調增的仍是單調減的。code

無論是操做1仍是操做2,咱們就是把[l,r]的全部元素合併到一棵線段樹,惟一麻煩的就是端點l,r多是在當前的某個區間裏面,這樣的話咱們還須要把這個區間給分裂,再把咱們須要的那部分拿出來合併blog

代碼實現是這樣的,注意咱們還要修改一下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這個結構體咱們採起右端點優先,左端點次一級的排序。內存

因爲升降序不一樣,咱們要從要分裂的區間的線段樹取出來的部分也不一樣,多是後面的,也多是前面的,這就須要分類討論一下了(不要怕麻煩,懶得話就不要學這種作法)get

還學到了一個回收空間內存的作法,具體時間就是用一個棧保存一下可使用的結點,每次要用結點的時候讓一個元素出棧便可,可見getnode函數string

#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;
}
相關文章
相關標籤/搜索