bzoj4552 [Tjoi2016&Heoi2016]排序 二分+线段树
初看很难做(当年考场上没人切)。
可以二分q位置的值。
大于二分值的记为1,其余的记为零。
用线段树维护一下区间求和和区间赋值就行了。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; inline int read(){ int x=0,f=1,ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-'){f=-1;}ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int data[800005],at[800005]; inline void pushdown(int id,int l,int r){ if(at[id]==-1) return ; data[id]=at[id]*(r-l+1); at[id<<1]=at[id<<1|1]=at[id]; at[id]=-1; } void add(int id,int l,int r,int sl,int sr,int i){ pushdown(id,l,r); if(l>sr||r<sl) return ; if(sl<=l&&r<=sr){ at[id]=i; pushdown(id,l,r); return ; } int mid=(l+r)>>1; add(id<<1,l,mid,sl,sr,i); add(id<<1|1,mid+1,r,sl,sr,i); data[id]=data[id<<1]+data[id<<1|1]; } int query(int id,int l,int r,int sl,int sr){ pushdown(id,l,r); if(l>sr||r<sl) return 0; if(sl<=l&&r<=sr) return data[id]; int mid=(l+r)>>1; return query(id<<1,l,mid,sl,sr)+query(id<<1|1,mid+1,r,sl,sr); } int n,m,q; int a[100005]; int op[100005],l[100005],r[100005]; inline bool ok(int x){ memset(at,-1,sizeof(at)); memset(data,0,sizeof(data)); int i,num; for(i=1;i<=n;i++) if(a[i]<=x) add(1,1,n,i,i,0); else add(1,1,n,i,i,1); for(i=1;i<=m;i++){ num=query(1,1,n,l[i],r[i]); if(op[i]==0) add(1,1,n,l[i],r[i]-num,0),add(1,1,n,r[i]-num+1,r[i],1); else add(1,1,n,l[i],l[i]+num-1,1),add(1,1,n,l[i]+num,r[i],0); } num=query(1,1,n,q,q); if(num) return false; else return true; } int main(){ n=read(),m=read();int i,sl=1,sr=n,mid; for(i=1;i<=n;i++) a[i]=read(); for(i=1;i<=m;i++) op[i]=read(),l[i]=read(),r[i]=read(); q=read(); while(sl<sr){ mid=(sl+sr)>>1; if(ok(mid)) sr=mid; else sl=mid+1; } printf("%d\n",sl); return 0; }