luogu P2824 [HEOI2016/TJOI2016]排序

在这里插入图片描述

analysis

这题思路很巧妙啊

关键点是能够想到对一个01序列的排序可以用log级别的线段树来操作

想到这点后,我们可以二分q位置上的数字,将原序列大于等于这个值的数字都写成1,其他的写成0,然后用线段树模拟排序就行

能够这样做的原因:假设我们二分的值是mid,这里的数字本来是x,那么当x>mid时,最后排序后的q处的数就等于1,反之等于0

实现的时候注意初始化和lazy函数的初值

#include<bits/stdc++.h>
using namespace std;
#define loop(i,start,end) for(register int i=start;i<=end;++i)
#define clean(arry,num) memset(arry,num,sizeof(arry))
#define anti_loop(i,start,end) for(register int i=start;i>=end;--i)
#define ll long long
#define isdegit(r) ((r>='0'&&r<='9'))
template<typename T>void read(T &x){
	x=0;char r=getchar();T neg=1;
	while(!isdegit(r)){if(r==45)neg=-1;r=getchar();}
	while(isdegit(r)){x=(x<<1)+(x<<3)+r-48;r=getchar();}
	x*=neg;
}
int n,m;
const int maxn=1e5+10,maxm=1e5+10;
int a[maxn];
struct _operator{
	int dir,l,r;
	_operator(int dir=0,int l=0,int r=0):dir(dir),l(l),r(r){}
}op[maxm];
int sum[maxn<<2],lazy[maxn<<2];
inline void pushup(int rt){sum[rt]=sum[rt<<1]+sum[rt<<1|1];}
inline void pushdown(int l,int r,int rt){
	if(lazy[rt]!=-1){
		lazy[rt<<1]=lazy[rt];
		lazy[rt<<1|1]=lazy[rt];
		int mid=(l+r)>>1;
		sum[rt<<1]=lazy[rt]*(mid-l+1);
		sum[rt<<1|1]=lazy[rt]*(r-(mid+1)+1);
		lazy[rt]=-1;
	}
}
void buildtree(int l,int r,int rt,int q){
	sum[rt]=0;lazy[rt]=-1;
	if(l>r)return;
	if(l==r){
		sum[rt]=(a[l]>=q);
		return;
	}
	int mid=(l+r)>>1;
	buildtree(l,mid,rt<<1,q);
	buildtree(mid+1,r,rt<<1|1,q);
	pushup(rt);
}
int querysum(int l,int r,int nl,int nr,int rt){
	if(l>r||nl>nr)return 0;
	if(l<=nl&&nr<=r)return sum[rt];
	int mid=(nl+nr)>>1;
	int res=0;
	pushdown(nl,nr,rt);
	if(mid>=l)res+=querysum(l,r,nl,mid,rt<<1);
	if(mid<r)res+=querysum(l,r,mid+1,nr,rt<<1|1);
	return res;
}
void update(int l,int r,int nl,int nr,int rt,int w){
	if(l>r||nl>nr)return;
	if(l<=nl&&nr<=r){
		lazy[rt]=w;
		sum[rt]=w*(nr-nl+1);
		return;
	}
	int mid=(nl+nr)>>1;
	pushdown(nl,nr,rt);
	if(mid>=l)update(l,r,nl,mid,rt<<1,w);
	if(mid<r)update(l,r,mid+1,nr,rt<<1|1,w);
	pushup(rt);
}
inline bool check(int mid,int q){
	buildtree(1,n,1,mid);
	loop(i,1,m){
		int k=querysum(op[i].l,op[i].r,1,n,1);
		if(op[i].dir==0)
			update(op[i].r-k+1,op[i].r,1,n,1,1),
			update(op[i].l,op[i].r-k,1,n,1,0);
		else if(op[i].dir==1)
			update(op[i].l+k,op[i].r,1,n,1,0),
			update(op[i].l,op[i].l+k-1,1,n,1,1);
	}
	return querysum(q,q,1,n,1)==1;
}
inline void bin(int q){
	int l,r,res;l=1,r=n;
	while(l<=r){
		int mid=(l+r)>>1;
		if(check(mid,q)){
			res=mid;
			l=mid+1;
		}
		else r=mid-1;
	}
	printf("%d\n",res);
}
int main(){
	#ifndef ONLINE_JUDGE
	freopen("datain.txt","r",stdin);
	#endif
	read(n),read(m);
	loop(i,1,n)read(a[i]);
	loop(i,1,m){
		int ops,l,r;
		read(ops),read(l),read(r);
		op[i]=_operator(ops,l,r);
	}
	int q;read(q);
	bin(q);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AndrewMe8211

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值