1. 程式人生 > >HDU 5649 DZY Loves Sorting(二分答案+線段樹、線段樹合並+線段樹分割)

HDU 5649 DZY Loves Sorting(二分答案+線段樹、線段樹合並+線段樹分割)

空間 namespace memset ons \n create name 題意 size

題意

一個 \(1\)\(n\) 的全排列,\(m\) 種操作,每次將一段區間 \([l,r]\) 按升序或降序排列,求 \(m\) 次操作後的第 \(k\) 位。

\(1 \leq n \leq 10^5\)

思路

兩個 \(\log\) 的做法展現了二分答案的強大功能。首先二分枚舉第 \(k\) 位的值,然後將小於等於它的數都變為 \(1\) ,大於它的數變為 \(0\) ,線段樹可以實現對 \(01\) 序列快速的排序,按要求進行排序,然後如果第 \(k\) 位為 \(1\) 說明這個數小於等於 \(k\) ,就這樣不斷二分下來,得到的邊界值就是第 \(k\) 位真實的值。這個做法是離線的,有兩個 \(\log\)

,但代碼好實現。

但這道題,有一個 \(\log\) 、在線的做法。考慮每個位置開一棵動點線段樹,把這個位置的數扔進線段樹,區間的排序直接用線段樹合並進行,但是如果區間的某個端點落在某一個完整的區間內,那就會破壞這個區間的單調性,所以還要線段樹分割。我們對於一個完整區間,存下是升序還是降序,然後“分割”出需要的元素,線段樹分割代碼如下:

void split(int &x,int y,int K,int l,int r)      //y拆前K個給x,合並前將初始x清零(x是一個空樹)
{
    create(x);
    if(l==r){sum[x]=sum[y],sum[y]=0;return;}
    int mid=(l+r)>>1;
    if(K<=sum[lson[y]])
    {
        split(lson[x],lson[y],K,l,mid);
        sum[x]=sum[lson[x]]+sum[rson[x]];
        sum[y]=sum[lson[y]]+sum[rson[y]];
        return;
    }
    split(rson[x],rson[y],K-sum[lson[y]],mid+1,r);
    lson[x]=lson[y],lson[y]=0;
    sum[x]=sum[lson[x]]+sum[rson[x]];
    sum[y]=sum[lson[y]]+sum[rson[y]];
}

和線段樹合並的寫法大致相同。

初始有 \(n\log n\) 個點,每次操作最多分割出 \(2\log n\) 個節點 ,所以空間復雜度為 \(O(n\log n)\)

合並初始的 \(n\) 個節點有一個 \(n\log n\) ,而分割的節點也最多是 \(2 n\log n\) ,所以時間復雜度也是 \(O(n\log n)\)

代碼

#include<bits/stdc++.h>
#define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i)
#define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i)
typedef long long LL;
using namespace std;
const int N=1e5+5;
const int NN=N*60;
bool mmr1;
int sum[NN],lson[NN],rson[NN];
int rt[N],tot;
void build()
{
    memset(rt,0,sizeof(rt));
    sum[tot=0]=lson[0]=rson[0]=0;
}
void create(int &k){if(!k)k=++tot,sum[k]=lson[k]=rson[k]=0;}
void update(int &k,int x,int l,int r)
{
    create(k);
    sum[k]++;
    if(l==r)return;
    int mid=(l+r)>>1;
    if(x<=mid)update(lson[k],x,l,mid);
    else update(rson[k],x,mid+1,r);
}
int query1(int k,int K,int l,int r)
{
    if(l==r)
    {
        if(sum[k]!=1)return -1;
        return l;
    }
    int mid=(l+r)>>1;
    if(K<=sum[lson[k]])return query1(lson[k],K,l,mid);
    else return query1(rson[k],K-sum[lson[k]],mid+1,r);
}
int query2(int k,int K,int l,int r)
{
    if(l==r)
    {
        if(sum[k]!=1)return -1;
        return l;
    }
    int mid=(l+r)>>1;
    if(K<=sum[rson[k]])return query2(rson[k],K,mid+1,r);
    else return query2(lson[k],K-sum[rson[k]],l,mid);
}
void merge(int &x,int y,int l,int r)                        //y並進x 
{
    if(!x||!y){x=(x|y);return;}
    if(l==r){sum[x]+=sum[y];return;}
    int mid=(l+r)>>1;
    merge(lson[x],lson[y],l,mid);
    merge(rson[x],rson[y],mid+1,r);
    sum[x]=sum[lson[x]]+sum[rson[x]];
}
void split1(int &x,int y,int K,int l,int r)     //y拆前K個給x
{
    create(x);
    if(l==r){sum[x]=sum[y],sum[y]=0;return;}
    int mid=(l+r)>>1;
    if(K<=sum[lson[y]])
    {
        split1(lson[x],lson[y],K,l,mid);
        sum[x]=sum[lson[x]]+sum[rson[x]];
        sum[y]=sum[lson[y]]+sum[rson[y]];
        return;
    }
    split1(rson[x],rson[y],K-sum[lson[y]],mid+1,r);
    lson[x]=lson[y],lson[y]=0;
    sum[x]=sum[lson[x]]+sum[rson[x]];
    sum[y]=sum[lson[y]]+sum[rson[y]];
}
void split2(int &x,int y,int K,int l,int r)     //y拆後K個給x
{
    create(x);
    if(l==r){sum[x]=sum[y],sum[y]=0;return;}
    int mid=(l+r)>>1;
    if(K<=sum[rson[y]])
    {
        split2(rson[x],rson[y],K,mid+1,r);
        sum[x]=sum[lson[x]]+sum[rson[x]];
        sum[y]=sum[lson[y]]+sum[rson[y]];
        return;
    }
    split2(lson[x],lson[y],K-sum[rson[y]],l,mid);
    rson[x]=rson[y],rson[y]=0;
    sum[x]=sum[lson[x]]+sum[rson[x]];
    sum[y]=sum[lson[y]]+sum[rson[y]];
}
set<int>st;
set<int>::iterator it,it1;
bool f[N];

int find_leftmost(int x)
{
    it=st.upper_bound(x);
    return *--it;
}
int find_rightmost(int x)
{
    it=st.upper_bound(x);
    return (*it)-1;
}
bool mmr2;

int main()
{
    int T,n,m,K;
    scanf("%d",&T);
    while(T--)
    {
        build();
        st.clear();
        memset(f,0,sizeof(f));
        scanf("%d%d",&n,&m);
        FOR(i,1,n)
        {
            int x;
            scanf("%d",&x);
            update(rt[i],x,1,n);
        }
        FOR(i,1,n+1)st.insert(i);
        
        while(m--)
        {
            int op,l,r;
            scanf("%d%d%d",&op,&l,&r);
            int L=find_leftmost(l);
            if(l!=L)
            {
                if(f[L]==0)rt[l]=0,split1(rt[l],rt[L],l-L,1,n);
                else rt[l]=0,split2(rt[l],rt[L],l-L,1,n);
                swap(rt[l],rt[L]);
                f[l]=f[L];
                st.insert(l);
            }
            
            int R=find_rightmost(r),_R=find_leftmost(r);
            if(r!=R)
            {
                f[r+1]=f[_R];
                if(f[_R]==0)rt[r+1]=0,split2(rt[r+1],rt[_R],R-r,1,n);
                else rt[r+1]=0,split1(rt[r+1],rt[_R],R-r,1,n);
                st.insert(r+1);
            }
            
            f[l]=op;
            it=st.find(l),it++;
            while((*it)<=r)
            {
                merge(rt[l],rt[*it],1,n);
                it1=it,it++,st.erase(it1);
            }
        }
        scanf("%d",&K);
        int x=find_leftmost(K);
        if(f[x]==0)printf("%d\n",query1(rt[x],K-x+1,1,n));
        else printf("%d\n",query2(rt[x],K-x+1,1,n));
    }
    return 0;
}

HDU 5649 DZY Loves Sorting(二分答案+線段樹、線段樹合並+線段樹分割)