题解 洛谷 P2824 [HEOI2016/TJOI2016]排序

洛 谷   P 2824   [ H E O I 2016 / T J O I 2016 ] 排 序 \color{#00F}{洛谷\ P2824\ [HEOI2016/TJOI2016]排序}  P2824 [HEOI2016/TJOI2016]

T i m e   L i m i t : 6.00 S \color{green}{Time\ Limit: 6.00S} Time Limit:6.00S
M e m o r y   L i m i t : 250.00 M B \color{green}{Memory\ Limit: 250.00MB} Memory Limit:250.00MB

P r o b l e m   D e s c r i p t i o n \color{blue}{Problem\ Description} Problem Description
洛谷 P2824 [HEOI2016/TJOI2016]排序
2016 2016 2016年,佳媛姐姐喜欢上了数字序列。因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题,需要你来帮助他。这个难题是这样子的:给出一个 1 1 1 n n n的全排列,现在对这个全排列序列进行 m m m次局部排序,排序分为两种:

  1. ( 0 , l , r ) (0,l,r) (0,l,r)表示将区间 [ l , r ] [l,r] [l,r]的数字升序排序
  2. ( 1 , l , r ) (1,l,r) (1,l,r)表示将区间 [ l , r ] [l,r] [l,r]的数字降序排序

最后询问第 q q q位置上的数字。

I n p u t \color{blue}{Input} Input
输入数据的第一行为两个整数 n n n m m m n n n表示序列的长度, m m m表示局部排序的次数。第二行为 n n n个整数,表示 1 1 1 n n n的一个全排列。接下来输入 m m m行,每一行有三个整数 o p op op, l l l, r r r, o p op op 0 0 0代表升序排序, o p op op 1 1 1代表降序排序, l l l r r r表示排序的区间。最后输入一个整数 q q q q q q表示排序完之后询问的位置。

O u t p u t \color{blue}{Output} Output
输出数据仅有一行,一个整数,表示按照顺序将全部的部分排序结束后第 q q q位置上的数字。

S a m p l e   I n p u t \color{blue}{Sample\ Input} Sample Input
6 3
1 6 2 5 3 4
0 1 4
1 3 6
0 2 4
3

S a m p l e   O u t p u t \color{blue}{Sample \ Output} Sample Output
5


由于将一个普通序列排序很慢,需要 n l o g n nlogn nlogn的时间,所以我们试着把它转化为对 01 01 01序列排序。先来考虑一个简单的问题:

如何将一个 01 01 01序列排序?( l o g n logn logn的复杂度)

对于这个问题,我们使用线段树来维护。查询一段区间内的 1 1 1的个数记为 c n t cnt cnt,如果是升序,就将这段区间的 [ r − c n t + 1 , r ] [r-cnt+1, r] [rcnt+1,r]都更改为 1 1 1,将 [ l , r − c n t ] [l, r-cnt] [l,rcnt]更改为 0 0 0。降序则将 [ l , l + c n t − 1 ] [l, l+cnt-1] [l,l+cnt1]更改为 1 1 1,将 [ l + c n t , r ] [l+cnt, r] [l+cnt,r]更改为 0 0 0。这样我们就成功地把排序转化为了区间查询和区间修改。

接下来我们来说本题的做法:

这是一个离线的做法。首先二分答案 m i d mid mid。我们把原排列中大于等于 m i d mid mid的数都标记为 1 1 1,小于 m i d mid mid的都标记为 0 0 0。然后对于每个操作我们就将 01 01 01序列排个序。最后如果第 p p p个位子仍是 1 1 1的话就是可行的。

这个二分成立因为是满足单调性的:可以简单地假设一下,如果你二分的答案是 1 1 1,那么原序列所有的值都转化为了 1 1 1,所以最后肯定是 t r u e true true。如果二分一个值成立当且仅当这个位子的值大于等于 m i d mid mid,故如果 c h e c k check check返回 t r u e true true,则 l = m i d + 1 l = mid+1 l=mid+1并且 a n s = m i d ans=mid ans=mid,否则 r = m i d − 1 r = mid-1 r=mid1

代码实现如下:

#include <bits/stdc++.h>
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;

const int MAXN=1e5+5;
int n,m,q;
int a[MAXN],sum[MAXN<<2],lazy[MAXN<<2];

struct Query
{
	int op;
	int l,r;
}que[MAXN];

inline int read()
{
	int X=0,f=1; char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') f=-1; ch=getchar();}
	while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+(ch^'0'); ch=getchar();}
	return X*f;
}

void pushup(int rt)
{
	sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}

void pushdown(int l,int r,int rt)
{
	if(lazy[rt]!=-1)
	{
		int mid=(l+r)>>1;
		sum[rt<<1]=(mid-l+1)*lazy[rt];
		lazy[rt<<1]=lazy[rt];
		sum[rt<<1|1]=(r-mid)*lazy[rt];
		lazy[rt<<1|1]=lazy[rt];
		lazy[rt]=-1;
	}
}

void buildtree(int m,int l,int r,int rt)
{
	lazy[rt]=-1;
	if(l==r)
	{
		sum[rt]=a[l]>=m;
		return;
	}
	int mid=(l+r)>>1;
	buildtree(m,lson);
	buildtree(m,rson);
	pushup(rt);
}

void updata(int L,int R,int k,int l,int r,int rt)
{
	if(L<=l&&r<=R)
	{
		sum[rt]=(r-l+1)*k;
		lazy[rt]=k;
		return;
	}
	pushdown(l,r,rt);
	int mid=(l+r)>>1;
	if(L<=mid) updata(L,R,k,lson);
	if(mid+1<=R) updata(L,R,k,rson);
	pushup(rt);
}

int query(int L,int R,int l,int r,int rt)
{
	if(L<=l&&r<=R) return sum[rt];
	pushdown(l,r,rt);
	int mid=(l+r)>>1,ans=0;
	if(L<=mid) ans+=query(L,R,lson);
	if(mid+1<=R) ans+=query(L,R,rson);
	return ans;
}

int queryPoint(int x,int l,int r,int rt)
{
	if(x==l&&r==x) return sum[rt];
	pushdown(l,r,rt);
	int mid=(l+r)>>1;
	if(x<=mid) return queryPoint(x,lson);
	if(mid+1<=x) return queryPoint(x,rson);
}

bool check(int mid)
{
	buildtree(mid,1,n,1);
	for(int i=1;i<=m;++i)
	{
		int cnt=query(que[i].l,que[i].r,1,n,1);
		if(que[i].op==0)
		{//当区间[l,r]中全部为1时,可能会出现que[i].l>que[i].r-cnt的情况,其他三种同理
			if(que[i].l<=que[i].r-cnt) updata(que[i].l,que[i].r-cnt,0,1,n,1);
			if(que[i].r-cnt+1<=que[i].r) updata(que[i].r-cnt+1,que[i].r,1,1,n,1);
		}
		else if(que[i].op==1)
		{
			if(que[i].l<=que[i].l+cnt-1) updata(que[i].l,que[i].l+cnt-1,1,1,n,1);
			if(que[i].l+cnt<=que[i].r) updata(que[i].l+cnt,que[i].r,0,1,n,1);
		}
	}
	return queryPoint(q,1,n,1);
}

void readdata()
{
	n=read(); m=read();
	for(int i=1;i<=n;++i) 
		a[i]=read();
	for(int i=1;i<=m;++i)
		que[i].op=read(),que[i].l=read(),que[i].r=read();
	q=read();
}

void work()
{
	int l=1,r=n,ans;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(check(mid)) l=mid+1,ans=mid;
		else r=mid-1;
	}
	printf("%d",ans);
}

int main()
{
//	freopen("input.txt","r",stdin);
	readdata();
	work();
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值