2023集 数据结构3 P2824 [HEOI2016/TJOI2016] 排序

[HEOI2016/TJOI2016] 排序

题目描述

2016 2016 2016 年,佳媛姐姐喜欢上了数字序列。因而她经常研究关于序列的一些奇奇怪怪的问题,现在她在研究一个难题,需要你来帮助她。

这个难题是这样子的:给出一个 1 1 1 n n n 的排列,现在对这个排列序列进行 m m m 次局部排序,排序分为两种:

  • 0 l r 表示将区间 [ l , r ] [l,r] [l,r] 的数字升序排序
  • 1 l r 表示将区间 [ l , r ] [l,r] [l,r] 的数字降序排序

注意,这里是对下标在区间 [ l , r ] [l,r] [l,r] 内的数排序。
最后询问第 q q q 位置上的数字。

输入格式

输入数据的第一行为两个整数 n n n m m m n n n 表示序列的长度, m m m 表示局部排序的次数。

第二行为 n n n 个整数,表示 1 1 1 n n n 的一个排列。

接下来输入 m m m 行,每一行有三个整数 op , l , r \text{op},l,r op,l,r op \text{op} op 0 0 0 代表升序排序, op \text{op} op 1 1 1 代表降序排序, l , r l,r l,r 表示排序的区间。

最后输入一个整数 q q q,表示排序完之后询问的位置

输出格式

输出数据仅有一行,一个整数,表示按照顺序将全部的部分排序结束后第 q q q 位置上的数字。

样例 #1

样例输入 #1

6 3
1 6 2 5 3 4
0 1 4
1 3 6
0 2 4
3

样例输出 #1

5

提示

河北省选2016第一天第二题。

对于 30 % 30\% 30% 的数据, n , m ≤ 1000 n,m\leq 1000 n,m1000

对于 100 % 100\% 100% 的数据, n , m ≤ 1 0 5 n,m\leq 10^5 n,m105 1 ≤ q ≤ n 1\leq q\leq n 1qn

思路

在这个题目中,我们要找的最终只是q所在的位置,所以可以想到二分答案,枚举mid,每次将大于等于mid的数定为1,小于mid的数设为0,每次建立线段树,将每次操作看为区间覆盖,由此便可以较为简单的得到答案

完整代码

#include<bits/stdc++.h>
using namespace std;
const int M=1e7+5;
#define int long long
int sm[M<<2],tag[M<<2],a[M];
bool vis[M];//tag数组存储着变为一还是变为零,但是由于tag初始化为0,
//所以使用vis数组来确定是否需要改变线段树上的点的值
#define lc ((x)<<1)
#define rc ((x)<<1|1)
struct node{
	int o,l,r;
};
node qs[M];
int n,m,q,maxn=0;
inline void build(int x,int l,int r,int k)//要注意这里的build有新的参数k
{
	vis[x]=0;//每次建树vis初始化为0
	if(l==r)
	{
		sm[x]=(a[l]>=k);//大于k变为1,小于k变为0
		return;
	} 
	int mid=(l+r)>>1;
	build(lc,l,mid,k);build(rc,mid+1,r,k);
	sm[x]=sm[lc]+sm[rc];
}
inline void cover(int x,int l,int r,long long d){
	sm[x]=(r-l+1)*d;
	tag[x]=d;
	vis[x]=1;
}//覆盖操作
inline void pushdown(int x,int l,int r)
{
	if(!vis[x])return;
	int mid=(l+r)>>1;
	cover(lc,l,mid,tag[x]);
	cover(rc,mid+1,r,tag[x]);
	tag[x]=0;
	vis[x]=0;//标记清除
}//标记传递
inline void update(int x,int l,int r,int L,int R,long long d)
{
	if(L<=l and r<=R) 
	{
		cover(x,l,r,d);
		return;
	}
	if (L>R) return;//进行特判
	int mid=(l+r)>>1;
	pushdown(x,l,r);
	if (L<=mid) update(lc,l,mid,L,R,d);
	if (mid+1<=R) update(rc,mid+1,r,L,R,d);
	sm[x]=sm[lc]+sm[rc];
}//区间修改
inline long long query(int x,int l,int r,int L,int R)
{
	int ans=0;
	if(l>=L&&r<=R)return sm[x];
	int mid=(l+r)>>1;
	pushdown(x,l,r);
	if(L<=mid)ans+=query(lc,l,mid,L,R);
	if(R>mid)ans+=query(rc,mid+1,r,L,R);
	sm[x]=sm[lc]+sm[rc];
	return ans;
}//区间查询
bool check(int d)
{
	build(1,1,n,d);
	int num;
	for(int i=1;i<=m;i++)
	{
		num=query(1,1,n,qs[i].l,qs[i].r);//因为线段树此时只有0,1,所以num为1的个数
		if(qs[i].o)
		{
			update(1,1,n,qs[i].l,qs[i].l+num-1,1);
			update(1,1,n,qs[i].l+num,qs[i].r,0);
		}//降序
		else
		{
			num=qs[i].r-qs[i].l+1-num;
			update(1,1,n,qs[i].l,qs[i].l+num-1,0);
			update(1,1,n,qs[i].l+num,qs[i].r,1);
		}//升序
	}
	return query(1,1,n,q,q);//操作完后查询a[q]的值
}//检查mid的取值大于还是小于a[q]
void solve()
{
	int l=1,r=maxn,ans;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(check(mid))
		{
			l=mid+1;
			ans=mid;
		}//如果mid的值大于等于a[q],就更新ans的值,并更新左端点
		else
			r=mid-1;
	}
	cout<<ans;
}//二分
signed main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		maxn=max(maxn,a[i]);//求出输入的数中的最大值,便于找寻mid
	}	
	for(int i=1;i<=m;i++)
		cin>>qs[i].o>>qs[i].l>>qs[i].r;
	cin>>q;//输入
	solve();//解决
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值