[HEOI2016/TJOI2016]排序

题目

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

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

0 l r 表示将区间 [l,r][l,r] 的数字升序排序
1 l r 表示将区间 [l,r][l,r] 的数字降序排序
注意,这里是对下标在区间 [l,r][l,r] 内的数排序。
最后询问第 qq 位置上的数字。

输入格式
输入数据的第一行为两个整数 nn 和 mm,nn 表示序列的长度,mm 表示局部排序的次数。

第二行为 nn 个整数,表示 11 到 nn 的一个排列。

接下来输入 mm 行,每一行有三个整数 \text{op},l,rop,l,r,\text{op}op 为 00 代表升序排序,\text{op}op 为 11 代表降序排序, l,rl,r 表示排序的区间。

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

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

输入输出样例
输入 #1复制
6 3
1 6 2 5 3 4
0 1 4
1 3 6
0 2 4
3
输出 #1复制
5
说明/提示
河北省选2016第一天第二题。

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

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

思路

直接做有一点不好做,考虑吧问题简单化:如果序列只有0和1怎么做
这就很显然了,线段树统计区间01个数,记一下哪里开始是0,哪里开始是1就行
回到原题,如何将题目转换为这样的模型
考虑二分一个mid,把<mid的看成0,反之就是1,然后就那样排序就行

代码

#include<bits/stdc++.h>
#define lc o<<1
#define rc o<<1|1
#define mid (l+r)/2
using namespace std;
const int N=1e5+77;
int n,m,p,T[4*N],lazy[4*N],a[N],ch[N],L[N],R[N];
void build(int o,int l,int r,int x)
{
	if(l==r)
	{
		T[o]=a[l]>=x; lazy[o]=0; return;
	}
	build(lc,l,mid,x); build(rc,mid+1,r,x);
	T[o]=T[lc]+T[rc]; lazy[o]=0;
}
void pushdown(int o,int l,int r)
{
	if(!lazy[o]) return;
	lazy[lc]=lazy[rc]=lazy[o];
	if(lazy[o] == 1)
	{
		T[lc]=mid-l+1; T[rc]=r-mid;
	}
	else T[lc]=T[rc]=0;
	lazy[o]=0;
}

int query(int o,int l,int r,int x,int y)
{
	if(x<=l&&y>=r) return T[o];
	if(x>r||y<l) return 0;
	pushdown(o,l,r);
	return query(lc,l,mid,x,y)+query(rc,mid+1,r,x,y);
}

int queryPoint(int o,int l,int r,int x)
{
	return query(o,l,r,x,x);
}

void update(int o,int l,int r,int x,int y,int val)
{
	if(x<=l&&y>=r){
		T[o] = val*(r-l+1); lazy[o] = val ? 1 : -1;
		return;
	}
	if(x > r || y < l) return;
	pushdown(o,l,r);
	update(lc,l,mid,x,y,val);
	update(rc,mid+1,r,x,y,val);
	T[o] = T[lc]+T[rc];
}

bool check(int x)
{
	build(1,1,n,x);
	for(int i = 1; i <= m; i ++){
		int cnt1 = query(1,1,n,L[i],R[i]);
		if(ch[i] == 0){
			update(1,1,n,R[i]-cnt1+1,R[i],1);
			update(1,1,n,L[i],R[i]-cnt1,0);
		}
		else{
			update(1,1,n,L[i],L[i]+cnt1-1,1);
			update(1,1,n,L[i]+cnt1,R[i],0);
		}
	}
	return queryPoint(1,1,n,p);
}

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",&ch[i],&L[i],&R[i]);
	scanf("%d",&p);
	int ll=1,rr=n,midd,ans;
	while(ll<=rr)
	{
		midd=(ll+rr)>>1;
		if(check(midd)) ans=midd,ll=midd+1; else rr=midd-1;
	}
	printf("%d",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值