P2824 [HEOI2016/TJOI2016] 排序

题目

P2824 [HEOI2016/TJOI2016] 排序_第1张图片

思路

直接模拟排序肯定会TLE,所以我们想一种离线的方法:01排序
利用二分答案check一下d,设序列中大于等于d的数为1,小于d的数为0
完成后再进行排序:这样升序排列就是将0放前面1放后面,降序排列则相反
“放”这一操作还可以优化:降序排序中,先输出一共有num个1,然后把前num个数设成1,后面的数设成0,升序排列则相反
在全部模拟完后,我们看看第q位上的数是0还是1,若是1,则代表 a q a_q aq是大于等于二分答案d,也就是说需要将l右移,反之左移
证明: a q > = d , 若使二分答案 d 更接近最终答案,需要将 d 增大,也就是 l = m i d + 1 a_q>=d,若使二分答案d更接近最终答案,需要将d增大,也就是l=mid+1 aq>=d,若使二分答案d更接近最终答案,需要将d增大,也就是l=mid+1
因为用到了区间操作,所以我们用线段树做
理论存在,实践开始

代码

#include
using namespace std;
#define int long long
#define INF 0x3f3f3f3f
const int M=1e7+5;
struct node{
	int opt,l,r;
}qs[M];
istream& operator >> (istream& in,node& t){
	in>>t.opt>>t.l>>t.r;
	return in;
}
int a[M];
int n,m;
int q;
int maxn=-INF;
struct seg{
	#define lc(x) x<<1
	#define rc(x) x<<1|1
	int tag[M<<2],v[M<<2];
	bool vis[M<<2];
	void cover(int x,int l,int r,int k){//记录tag+更改操作
		tag[x]=k,vis[x]=1;
		v[x]=k*(r-l+1);
	}
	void pushup(int x) { v[x]=v[lc(x)]+v[rc(x)]; }//向上更改
	void pushdown(int x,int l,int r){//向下更改
		if(!vis[x]) return;
		int mid=l+r>>1;
		cover(lc(x),l,mid,tag[x]),cover(rc(x),mid+1,r,tag[x]);
		tag[x]=0,vis[x]=0;
	}
	void build(int x,int l,int r,int k){//建树
		vis[x]=0;
		if(l==r) { v[x]=(a[l]>=k);return; }
		int mid=l+r>>1;
		build(lc(x),l,mid,k),build(rc(x),mid+1,r,k);
		pushup(x);
	}
	int query(int ql,int qr,int x,int l,int r){//查询区间和的数量
		if(ql>qr) return 0;//特殊情况判断
		if(ql<=l&&r<=qr) return v[x];
		int ans=0;
		int mid=l+r>>1;
		pushdown(x,l,r);
		if(ql<=mid) ans+=query(ql,qr,lc(x),l,mid);//特殊情况
		if(mid+1<=qr) ans+=query(ql,qr,rc(x),mid+1,r);
		pushup(x);
		return ans;
	}
	void update(int ql,int qr,int x,int l,int r,int k){
		if(ql>qr) return;
		if(ql<=l&&r<=qr) { cover(x,l,r,k);return; }
		pushdown(x,l,r);
		int mid=l+r>>1;
		if(ql<=mid) update(ql,qr,lc(x),l,mid,k);
		if(mid+1<=qr) update(ql,qr,rc(x),mid+1,r,k);
		pushup(x);
	}
}st;
bool check(int d){
	st.build(1,1,n,d);
	int num;
	for(int i=1;i<=m;i++){
		int l=qs[i].l;int r=qs[i].r;
		num=st.query(l,r,1,1,n);
		switch (qs[i].opt){
			case 1:st.update(l,l+num-1,1,1,n,1),st.update(l+num,r,1,1,n,0);break;//升序(前1后0)
			case 0:num=r-l+1-num;st.update(l,l+num-1,1,1,n,0),st.update(l+num,r,1,1,n,1);break;//降序(前0后1)
		}
	}
	return st.query(q,q,1,1,n);//查询a[q]的值
}
signed main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i],maxn=max(maxn,a[i]);
	for(int i=1;i<=m;i++) cin>>qs[i];
	cin>>q;
	int l=1,r=maxn,ans;
	while(l<=r){
		int mid=l+r>>1;
		if(check(mid)) l=mid+1,ans=mid;
		else r=mid-1;
	}
	cout<<ans;
	return 0;
}

你可能感兴趣的:(算法,c++,刘汝佳,二分,排序,线段树,数据结构)