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;
}

浙公网安备 33010602011771号