HDU 5649 DZY Loves Sorting(二分答案+線段樹、線段樹合並+線段樹分割)
阿新 • • 發佈:2018-12-27
空間 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(二分答案+線段樹、線段樹合並+線段樹分割)