1. 程式人生 > >[Luogu P2824] [HEOI2016/TJOI2016]排序 (線段樹+二分答案)

[Luogu P2824] [HEOI2016/TJOI2016]排序 (線段樹+二分答案)

題面

傳送門:https://www.luogu.org/problemnew/show/P2824


Solution

這題極其巧妙。

 

首先,如果直接做m次排序,顯然會T得起飛。

注意一點:我們只需要找到一個數。

所以說,我們可以考慮一個絕妙的想法:我們可以用二分答案的方法縮小要找的數的區間

考慮二分一個值,判定p位置的數排序之後,p位置上的數是否>=mid

如果>=mid,則向右找,否則向左找。

 

怎麼判定p位置的數排序之後是否>=mid呢?

考慮這樣做:掃描一遍原陣列,>=mid的數賦值為1,<mid的數賦值為0。

這樣子,題目就變成了一個01序列排序。

這就很可做了,我們直接線段樹維護之即可,我們只需要實現區間查詢與區間賦值。

對於一個01區間排序,我們只需要知道這個區間有多少個0,多少個1,然後區間修改即可。

 

時間複雜度O(m*logn^2)

 

就醬,這題就可以切掉啦(ノ´▽`)ノ♪


 

Code

//Luogu  P2824 [HEOI2016/TJOI2016]排序
//Oct,19th,2018
//二分答案縮小範圍+線段樹妙題
#include<iostream>
#include<cstdio>
using namespace std;
long long read()
{
    
long long x=0,f=1; char c=getchar(); while(!isdigit(c)){if(c=='-')f=-1;c=getchar();} while(isdigit(c)){x=x*10+c-'0';c=getchar();} return x*f; } const int N=30000+100; int a[N],w[N]; struct SegmentTree { #define lson (now<<1) #define rson (now<<1|1) #define mid ((now_l+now_r)>>1) static
const int M=N<<2; int sum[M][2],lazy[M]; inline void update(int now) { sum[now][0]=sum[lson][0]+sum[rson][0]; sum[now][1]=sum[lson][1]+sum[rson][1]; } inline void pushdown(int now,int now_l,int now_r) { if(now_l==now_r) { lazy[now]=2; return; } lazy[lson]=lazy[rson]=lazy[now]; sum[lson][lazy[now]]=mid-now_l+1,sum[lson][!lazy[now]]=0; sum[rson][lazy[now]]=now_r-mid,sum[rson][!lazy[now]]=0; lazy[now]=2; } void Build(int now,int now_l,int now_r) { sum[now][0]=sum[now][1]=0; lazy[now]=2; if(now_l==now_r) { sum[now][w[now_l]]++; return; } Build(lson,now_l,mid); Build(rson,mid+1,now_r); update(now); } void Change(int L,int R,int x,int now,int now_l,int now_r) { if(L>R) return; if(lazy[now]!=2) pushdown(now,now_l,now_r); if(now_l>=L and now_r<=R) { sum[now][x]=now_r-now_l+1,sum[now][!x]=0; lazy[now]=x; return; } if(L<=mid) Change(L,R,x,lson,now_l,mid); if(R>mid) Change(L,R,x,rson,mid+1,now_r); update(now); } int Query(int L,int R,int x,int now,int now_l,int now_r) { if(lazy[now]!=2) pushdown(now,now_l,now_r); if(now_l>=L and now_r<=R) return sum[now][x]; int ans=0; if(L<=mid) ans+=Query(L,R,x,lson,now_l,mid); if(R>mid) ans+=Query(L,R,x,rson,mid+1,now_r); return ans; } #undef lson #undef rson #undef mid }sgt; struct OP { int type,L,R; }op[N]; int n,m,p; bool Check(int x) { for(int i=1;i<=n;i++) if(a[i]>=x) w[i]=1; else w[i]=0; sgt.Build(1,1,n); for(int i=1;i<=m;i++) { int cnt0=sgt.Query(op[i].L,op[i].R,0,1,1,n),cnt1=op[i].R-op[i].L+1-cnt0; if(op[i].type==0) sgt.Change(op[i].L,op[i].L+cnt0-1,0,1,1,n), sgt.Change(op[i].L+cnt0,op[i].R,1,1,1,n); else sgt.Change(op[i].L,op[i].L+cnt1-1,1,1,1,n), sgt.Change(op[i].L+cnt1,op[i].R,0,1,1,n); } if(sgt.Query(p,p,1,1,1,n)==1) return true; return false; } int main() { n=read(),m=read(); for(int i=1;i<=n;i++) a[i]=read(); for(int i=1;i<=m;i++) op[i].type=read(),op[i].L=read(),op[i].R=read(); p=read(); int L=0,R=n+100,ans=0; while(L<=R) { int mid=(L+R)/2; if(Check(mid)==true) ans=max(ans,mid),L=mid+1; else R=mid-1; } printf("%d",ans); return 0; }