Luogu P2824 排序 题解 [ 紫 ] [ 线段树 ] [ 二分 ] [ adhoc ]

排序:二分线段树神仙好题。

trick

我们可以二分值域,然后把大于等于它的数标记成 \(1\),其他标记为 \(0\)(有些题需要标记成 \(-1\) ),然后根据这个来 check 方案是否可行,这通常通过判断某个数是否是 \(1\) 来实现。本质上其实就是 check 大于等于它的数能否成为答案(大于等于它的数为 \(1\))。常用于查找中位数、第 \(k\) 个数,以及大小关系只注重两种(比如只区分大于 \(7\) 和小于 \(7\) ,而大于 \(7\) 的数之间的大小无关的情况)。

运用到和这道题类似的 trick 的题还有这几道:最大战力Median Pyramid Hard

思路

知道这个 trick 之后这题就迎刃而解了。

因为这是个排列,所以我们直接二分 \(1\)\(n\) 中的数,把大于等于它的数标记成 \(1\) ,其余为 \(0\)

在 check 中,我们用线段树维护一个 \(01\) 序列,处理好区间查询与区间的 \(01\) 排序。

最后 check 时查询线段树上的第 \(q\) 项是多少,如果是 \(1\) 就说明取的值小于等于答案(因为此时标记为 \(1\) 的太多了),否则就说明我们取的值比答案大(标记为 \(1\) 的太少了)。最后输出最后一个第 \(q\) 位是 \(1\) 的数即可。

代码

#include <bits/stdc++.h>
#define fi first
#define se second
#define lc (p<<1)
#define rc ((p<<1)|1)
using namespace std;
typedef long long ll;
typedef pair<int,int> pi;
const int N=100005;
int n,m,a[N],ql[N],qr[N],qop[N],q;
struct node{
	int l,r;
	int sum1,sum0;
	int tag;
}tr[4*N];
void pushup(node &p,node ls,node rs)
{
	p.sum1=ls.sum1+rs.sum1;
	p.sum0=ls.sum0+rs.sum0;
}
void pd(int p,int op)
{
	node &t=tr[p];
	if(op==0)
	{
		t.sum1=0;
		t.sum0=t.r-t.l+1;
		t.tag=0;
	}
	if(op==1)
	{
		t.sum0=0;
		t.sum1=t.r-t.l+1;
		t.tag=1;
	}
}
void pushdown(int p)
{
	if(tr[p].tag==0)
	{
		pd(lc,0);
		pd(rc,0);
	}
	if(tr[p].tag==1)
	{
		pd(lc,1);
		pd(rc,1);
	}
	tr[p].tag=-1;
}
void build(int p,int ln,int rn,int x)
{
	tr[p]={ln,rn,a[ln]>=x,a[ln]<x,-1};
	if(ln==rn)return;
	int mid=(ln+rn)>>1;
	build(lc,ln,mid,x);
	build(rc,mid+1,rn,x);
	pushup(tr[p],tr[lc],tr[rc]);
}
void update(int p,int ln,int rn,int op)
{
	if(ln<=tr[p].l&&tr[p].r<=rn)
	{
		pd(p,op);
		return;
	}
	pushdown(p);
	int mid=(tr[p].l+tr[p].r)>>1;
	if(ln<=mid)update(lc,ln,rn,op);
	if(rn>=mid+1)update(rc,ln,rn,op);
	pushup(tr[p],tr[lc],tr[rc]);
}
node query(int p,int ln,int rn)
{
	if(ln<=tr[p].l&&tr[p].r<=rn)return tr[p];
	int mid=(tr[p].l+tr[p].r)>>1;
	pushdown(p);
	if(rn<=mid)return query(lc,ln,rn);
	if(ln>=mid+1)return query(rc,ln,rn);
	node tmp;
	pushup(tmp,query(lc,ln,rn),query(rc,ln,rn));
	return tmp;
}
bool check(int x)
{
	build(1,1,n,x);
//	for(int i=1;i<=n;i++)cout<<query(1,i,i).sum1<<' ';
//	cout<<endl;
	for(int i=1;i<=m;i++)
	{
		node res=query(1,ql[i],qr[i]);
		int sum1=res.sum1,sum0=res.sum0;
		if(qop[i]==0)
		{
			if(ql[i]<=ql[i]+sum0-1)update(1,ql[i],ql[i]+sum0-1,0);
			if(ql[i]+sum0<=qr[i])update(1,ql[i]+sum0,qr[i],1);
		}
		else
		{
			if(ql[i]<=ql[i]+sum1-1)update(1,ql[i],ql[i]+sum1-1,1);
			if(ql[i]+sum1<=qr[i])update(1,ql[i]+sum1,qr[i],0);
		}
//		for(int i=1;i<=n;i++)cout<<query(1,i,i).sum1<<' ';
//		cout<<endl;		
	}
	return (query(1,q,q).sum1==1);
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=m;i++)cin>>qop[i]>>ql[i]>>qr[i];
	cin>>q;
	int l=1,r=n,mid;
	while(l<r)
	{
		mid=(l+r+1)>>1;
		if(check(mid))l=mid;
		else r=mid-1;
//		cout<<"l="<<l<<" ; r="<<r<<" ; mid="<<mid<<endl;
//		for(int i=1;i<=n;i++)cout<<query(1,i,i).sum1<<' ';
//		cout<<endl<<endl;
	}
	cout<<l;
	return 0;
}

posted @ 2024-08-28 15:44  KS_Fszha  阅读(3)  评论(0编辑  收藏  举报