【题解】洛谷 P2824 排序

假的题目传送门[https://www.luogu.com.cn/problem/P2824]

题目描述:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p9Seo1j8-1607157810430)(C:\Users\Stuedent\AppData\Roaming\Typora\typora-user-images\image-20201201210311044.png)]


Solution

乍一看这道题跟CF558E长得非常像,我们能否采用解决这道题的方法来解决这道题呢?

答案:不行。

CF558E那道题排序过程之所以能用线段树维护是因为排序的值域是确定的,只有26,因此我们暴力开26棵线段树维护状态没有问题。但是这道题显然就不行了,值域为n,我们开不下。

这时我们的思路就向CF558E那道题的思路去靠近,我们能否通过一些特殊的手段来限制值域从而是排序过程变得可以维护呢?

最简单的值域:0/1

由于查询只有一次,我们想到这里之后就感觉这道题变得可做了起来。

我们考虑如何将几个数的值域变成0/1----->二分答案。

二分枚举最终位置的数是否能够>=x。

过程中将大于等于x的数变成1,小于x的数变成0。

于是问题就变成了0/1的排序,这样问题就变得比CF558E简单多了。

是否满足条件只需要看那个位置的数是否为1即可。

中间用线段树维护,具体过程不讲了。

二分答案不断放缩的过程中,我们可以得到最终答案。


Code

#include<bits/stdc++.h>
using namespace std;

const int N = 2e5+100;
int n,m,po;
int a[N];
struct Node{
	int op,l,r;
}q[N];

struct Tr{
	struct Node{
		int tag,val;
	}tr[4*N];
	
	void spread(int x,int l,int r){
		if (tr[x].tag == 0) return;
		int mid = l + r >> 1;
		tr[x<<1].tag = tr[x<<1|1].tag = tr[x].tag;
		if (tr[x].tag == 1) tr[x<<1].val = mid-l+1 , tr[x<<1|1].val = r-mid;
		if (tr[x].tag == -1) tr[x<<1].val = tr[x<<1|1].val = 0;
		tr[x].tag = 0; return ;
	}
	void Change(int x,int l,int r,int L,int R,int v){
		if (L <= l && r <= R){
			tr[x].tag = v;
			if (v == 1) tr[x].val = r-l+1;
			if (v == -1) tr[x].val = 0;
//			cout<<"OK"<<' '<<l<<' '<<r<<' '<<tr[x].val<<endl;
			return;
		}
		spread(x,l,r); int mid = l+r>>1;
		if (L <= mid) Change(x<<1,l,mid,L,R,v);
		if (R > mid) Change(x<<1|1,mid+1,r,L,R,v);
		tr[x].val = tr[x<<1].val + tr[x<<1|1].val;
	}
	int Ask(int x,int l,int r,int L,int R){
//			cout<<"x= "<<x<<' '<<"l = "<<l<<' '<<"r = "<<r<<' '<<"tag = "<<tr[x].tag<<' '<<tr[x].val<<endl;
		if (L <= l && r <= R){
			return tr[x].val;
		}
		spread(x,l,r); int mid = l+r>>1;
		int sum = 0;
		if (L <= mid) sum+=Ask(x<<1,l,mid,L,R);
		if (R > mid) sum+=Ask(x<<1|1,mid+1,r,L,R);
		return sum;
	}
}tr;

bool Check(int x){
	for (int i = 1; i <= n; i++) tr.Change(1,1,n,i,i,-1);
	for (int i = 1; i <= n; i++) if (a[i]>=x) tr.Change(1,1,n,i,i,1);
//	cout<<"x = "<<x<<endl;
	for (int i = 1; i <= m; i++){
		int num = tr.Ask(1,1,n,q[i].l,q[i].r);
//		cout<<"num = "<<num<<endl;
		if (num == 0) continue;
		if (q[i].op == 1){
			tr.Change(1,1,n,q[i].l,q[i].l+num-1,1);
			tr.Change(1,1,n,q[i].l+num,q[i].r,-1);
		}
		else {
//			cout<<"l = "<<q[i].l<<' '<<"r = "<<q[i].r-num<<endl;
			tr.Change(1,1,n,q[i].r-num+1,q[i].r,1);
			tr.Change(1,1,n,q[i].l,q[i].r-num,-1);
		}
//	for (int i = 1; i <= n; i++) cout<<"i = "<<i<<' '<<tr.Ask(1,1,n,i,i)<<endl;
	}
//	for (int i = 1; i <= n; i++) cout<<"i = "<<i<<' '<<tr.Ask(1,1,n,i,i)<<endl;
	return tr.Ask(1,1,n,po,po) == 1;
}

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",&q[i].op,&q[i].l,&q[i].r);
	scanf("%d",&po);
	int l = 1 , r = n;
//	tr.Change(1,1,n,2,2,1); tr.Change(1,1,n,4,4,1); tr.Change(1,1,n,1,2,-1); tr.Change(1,1,n,3,4,1);
//	for (int i = 1; i <= n; i++) cout<<"i = "<<i<<' '<<tr.Ask(1,1,n,i,i)<<endl;
//	cout<<Check(5)<<endl;
	while (l+1<r){
		int mid = l+r>>1;
		if (Check(mid)) l = mid; else r = mid;
	}
	printf("%d",Check(r)?r:l);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值