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

  

posted @ 2017-03-02 20:09  古城独钓  阅读(101)  评论(0编辑  收藏  举报