1. 程式人生 > >bzoj 4552: [Tjoi2016&Heoi2016]排序——二分+線段樹

bzoj 4552: [Tjoi2016&Heoi2016]排序——二分+線段樹

uil algorithm put 研究 難題 次數 modify none build

Description

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

Input

輸入數據的第一行為兩個整數n和m。n表示序列的長度,m表示局部排序的次數。1 <= n, m <= 10^5第二行為n個整 數,表示1到n的一個全排列。接下來輸入m行,每一行有三個整數op, l, r, op為0代表升序排序,op為1代表降序
排序, l, r 表示排序的區間。最後輸入一個整數q,q表示排序完之後詢問的位置, 1 <= q <= n。1 <= n <= 10^5 ,1 <= m <= 10^5

Output

輸出數據僅有一行,一個整數,表示按照順序將全部的部分排序結束後第q位置上的數字。

Sample Input

6 3
1 6 2 5 3 4
0 1 4
1 3 6
0 2 4
3

Sample Output

5 ———————————————————————————————— 二分p位置的值,把大於mid的數改為1,小於等於mid的數改為0, 變成01串後就可以用線段樹實現排序了,
像降序升序什麽的操作就把1和0各堆到一邊就可以辣 排序後如果p的位置上的數為0,說明答案比mid小,如果為1,說明答案比mid大。 至於為什麽可以用二分呢 你想如果p位置上是1,說明mid較小,v[p]>mid,所以把v[p]給標記成了1。 如果p位置上是0,就是把v[p]<=mid,所以把v[p]標記成了0, 但是這樣還有一些大於v[p]的位置也是0,所以繼續往小的地方逼近答案。 滿足單調所以就可以這麽寫辣 技術分享
#include<cstdio>
#include<cstring>
#include<algorithm>
const int M=5e5+7
; int read(){ int ans=0,f=1,c=getchar(); while(c<0||c>9){if(c==-) f=-1; c=getchar();} while(c>=0&&c<=9){ans=ans*10+(c-0); c=getchar();} return ans*f; } int p,n,m,v[M]; int lx[M],rx[M],op[M]; int L,R,mx; struct pos{int s,h[2];}tr[M]; void up(int x){tr[x].s=tr[x<<1].s+tr[x<<1^1].s;} void down(int x,int l,int r){ if(l==r) return ; int ls=x<<1,rs=x<<1^1,mid=(l+r)>>1; if(tr[x].h[0]){ tr[x].h[0]=0; tr[ls].h[0]=tr[rs].h[0]=1; tr[ls].s=tr[rs].s=0; tr[ls].h[1]=tr[rs].h[1]=0; } else if(tr[x].h[1]){ tr[x].h[1]=0; tr[ls].h[1]=tr[rs].h[1]=1; tr[ls].s=mid-l+1; tr[rs].s=r-mid; tr[ls].h[0]=tr[rs].h[0]=0; } } void build(int x,int l,int r){ tr[x].h[0]=tr[x].h[1]=0; if(l==r){ tr[x].s=(v[l]>mx); return ; } int mid=(l+r)>>1; build(x<<1,l,mid); build(x<<1^1,mid+1,r); up(x); } void modify(int x,int l,int r,int v){ if(L<=l&&r<=R){ tr[x].h[v]=1; tr[x].h[v^1]=0; tr[x].s=(r-l+1)*v; return ; } down(x,l,r); int mid=(l+r)>>1; if(L<=mid) modify(x<<1,l,mid,v); if(R>mid) modify(x<<1^1,mid+1,r,v); up(x); } int query(int x,int l,int r){ if(L<=l&&r<=R) return tr[x].s; down(x,l,r); int mid=(l+r)>>1,sum=0; if(L<=mid) sum+=query(x<<1,l,mid); if(R>mid) sum+=query(x<<1^1,mid+1,r); return sum; } bool check(int k){ mx=k; build(1,1,n); for(int i=1;i<=m;i++){ L=lx[i]; R=rx[i]; int ly=query(1,1,n); if(op[i]){ if(ly) L=lx[i],R=lx[i]+ly-1,modify(1,1,n,1); if(lx[i]+ly<=rx[i]) L=lx[i]+ly,R=rx[i],modify(1,1,n,0); } else{ if(lx[i]<=rx[i]-ly) L=lx[i],R=rx[i]-ly,modify(1,1,n,0); if(ly) L=rx[i]-ly+1,R=rx[i],modify(1,1,n,1); } } L=R=p; return !query(1,1,n); } int main(){ n=read(); m=read(); for(int i=1;i<=n;i++) v[i]=read(); for(int i=1;i<=m;i++) op[i]=read(),lx[i]=read(),rx[i]=read(); p=read(); int l=1,r=n; while(l<r){ int mid=(l+r)>>1; if(check(mid)) r=mid; else l=mid+1; }printf("%d\n",l); return 0; }
View Code

bzoj 4552: [Tjoi2016&Heoi2016]排序——二分+線段樹