[题解] P2824 [HEOI2016/TJOI2016]排序

传送门

一道非常巧妙的线段树题 && 思维题

首先题意非常好理解,最暴力的做法无非是根据操作依次排序...然而我没试过多少分啊,可能全wa,毕竟是省选题。

这就是本题的重点:二分

  • 二分第 q 个位置的值,把序列中所有大于等于 mid 的全部设成 1 ,小于的设成 0,我们由于并不在乎其他的值是什么,在哪里。
  • 所以最后使得 q 恰好等于 1 的mid就是答案。

考虑怎么维护

设计到区间修改,这就是一道非常完美的线段树题。

需要统计区间内 1 的个数

  • 对于升序排序:只需要把 1 全放在右边。

  • 对于逆序排序:跟升序排序相反。

一道省选难度的题,被我们变成了线段树的板子题。

以后碰到排序的题就需要增加 0/1 序列的意识了。
上代码:

#include <iostream>
#include <cstdio>
#define mid ((l+r)>>1)
#define ls p<<1
#define rs p<<1|1
using namespace std;
const int maxn=1e5 + 10;
int n,m,q;
int T[maxn<<2],tag[maxn<<2];
int a[maxn],op[maxn],L[maxn],R[maxn];
void build(int p,int l,int r,int x){
	if(l==r){
		T[p]= a[l]>=x;
		tag[p]=0;//归零好习惯 
		return;
	}
	build(ls,l,mid,x);build(rs,mid+1,r,x);
	T[p]=T[ls]+T[rs];tag[p]=0;
}
void pushdown(int p,int l,int r){
	if(!tag[p])return;
	tag[ls]=tag[rs]=tag[p];
	if(tag[p]==1){
		T[ls]=mid-l+1,T[rs]=r-mid;
	}
	else T[ls]=T[rs]=0;
	tag[p]=0;
}
int query(int p,int l,int r,int x,int y){
	if(x<=l&&r<=y)return T[p];
	if(x>r||y<l)return 0;
	pushdown(p,l,r);
	return query(ls,l,mid,x,y)+query(rs,mid+1,r,x,y);
}
int query_point(int p,int l,int r,int x){
	if(l==x&&r==x)return T[p];
	pushdown(p,l,r);
	if(x<=mid)return query_point(ls,l,mid,x);
	else return query_point(rs,mid+1,r,x);
}
void update(int p,int l,int r,int x,int y,int val){
	if(x<=l&&r<=y){
		T[p]=val*(r-l+1);tag[p]=val?1:-1;
		return;
	}
	if(x>r||y<l)return;
	pushdown(p,l,r);
	update(ls,l,mid,x,y,val);
	update(rs,mid+1,r,x,y,val);
	T[p]=T[ls]+T[rs];
}
bool check(int x){
	build(1,1,n,x);
	for(int i=1;i<=m;i++){
		int cnt1=query(1,1,n,L[i],R[i]);
		if(op[i]==0){
			update(1,1,n,R[i]-cnt1+1,R[i],1);
			update(1,1,n,L[i],R[i]-cnt1,0);
		}
		else{
			update(1,1,n,L[i],L[i]+cnt1-1,1);
			update(1,1,n,L[i]+cnt1,R[i],0);
		}
	}
	return query_point(1,1,n,q);
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)scanf("%d",a+i);
	for(int i=1;i<=m;i++){
		scanf("%d%d%d",op+i,L+i,R+i);
	}
	scanf("%d",&q);
	int ll=1,rr=n,midd,ans;
	while(ll<=rr){
		midd=(ll+rr)>>1;
		if(check(midd))ans=midd,ll=midd+1;
		else rr=midd-1;
	}
	printf("%d",ans);
	return 0;
}
posted @ 2021-08-12 16:34  ¶凉笙  阅读(24)  评论(0编辑  收藏  举报