【LOJ2055】【洛谷P2824】排序【线段树】【二分答案】

题目大意:

题目链接:
洛谷:https://www.luogu.org/problem/P2824
LOJ:https://loj.ac/problem/2055#submit_code
给出一个1n1\sim n的排列,两个操作:

  • 0 l r0\ l\ r:将[l,r][l,r]的数字升序排列
  • 1 l r1\ l\ r:将[l,r][l,r]的数字降序排列

给出qq,求出最终位于数列第qq位的数字。


思路:

nbnb思路!
暴力的复杂度时O(nmlogn)O(nm\log n)的,其中排序复杂度O(nlogn)O(n\log n),如果可以有效减少排序的复杂度,就可以降低代码复杂度。
我们发现,01序列的排序可以在O(logn)O(\log n)内完成。以升序排列为例,我们用线段树维护出区间[l,r][l,r]数字1的个数sumsum,然后区间修改[l,rsum+1][l,r-sum+1]为1,[l,l+sum][l,l+sum]为0。 .
那么如何把原数列转换成01数列呢?考虑二分答案。
我们在区间[1,n][1,n]内二分答案midmid,每次把大于等于midmid的数字变成1,小于midmid的数字变成0,然后O(mlogn)O(m\log n)完成所有操作,最终如果在第qq为上的数字是1,那么最终答案一定大于等于midmid,否则小于midmid
时间复杂度O(mlog2n)O(m\log^2n)


代码:

#include <cstdio>
#include <string>
#define reg register
using namespace std;

const int N=100010;
int n,m,opt,l,r,mid,x,y,q,a[N],b[N];

struct Ask
{
	int l,r,opt;
}ask[N];

struct Tree
{
	int l,r,lazy,sum;
}tree[N*4];

int read()
{
	int d=0;
	char ch=getchar();
	while (!isdigit(ch)) ch=getchar();
	while (isdigit(ch))
		d=(d<<3)+(d<<1)+ch-48,ch=getchar();
	return d;
}

void build(int x)
{
	tree[x].lazy=-1; tree[x].sum=0;
	if (tree[x].l==tree[x].r)
	{
		tree[x].sum=a[tree[x].l];
		return;
	}
	int mid=(tree[x].l+tree[x].r)>>1;
	tree[x*2].l=tree[x].l;
	tree[x*2].r=mid;
	tree[x*2+1].l=mid+1;
	tree[x*2+1].r=tree[x].r;
	build(x*2); build(x*2+1);
	tree[x].sum=tree[x*2].sum+tree[x*2+1].sum;
}

void pushdown(int x)
{
	if (tree[x].lazy!=-1)
	{
		tree[x*2].lazy=tree[x].lazy;
		tree[x*2+1].lazy=tree[x].lazy;
		tree[x*2].sum=tree[x].lazy*(tree[x*2].r-tree[x*2].l+1);
		tree[x*2+1].sum=tree[x].lazy*(tree[x*2+1].r-tree[x*2+1].l+1);
		tree[x].lazy=-1;
	}
}

int find(int x,int l,int r)
{
	if (l>r) return 0;
	if (tree[x].l==l && tree[x].r==r)
		return tree[x].sum;
	pushdown(x);
	int mid=(tree[x].l+tree[x].r)>>1;
	if (r<=mid) return find(x*2,l,r);
	if (l>mid) return find(x*2+1,l,r);
	return find(x*2,l,mid)+find(x*2+1,mid+1,r);
}

void change(int x,int l,int r,int val)
{
	if (l>r) return;
	if (tree[x].l==l && tree[x].r==r)
	{
		tree[x].sum=val*(tree[x].r-tree[x].l+1);
		tree[x].lazy=val;
		return;
	}
	pushdown(x);
	int mid=(tree[x].l+tree[x].r)>>1;
	if (r<=mid) change(x*2,l,r,val);
	else if (l>mid) change(x*2+1,l,r,val);
	else change(x*2,l,mid,val),change(x*2+1,mid+1,r,val);
	tree[x].sum=tree[x*2].sum+tree[x*2+1].sum;
}

int main()
{
	n=read(); m=read();
	for (reg int i=1;i<=n;i++)
		b[i]=read();
	for (reg int i=1;i<=m;i++)
		ask[i].opt=read(),ask[i].l=read(),ask[i].r=read();
	q=read();
	l=1; r=n;
	while (l<=r)
	{
		mid=(l+r)>>1;
		for (reg int i=1;i<=n;i++)
			if (b[i]<mid) a[i]=0;
				else a[i]=1;
		tree[1].l=1; tree[1].r=n;
		build(1);
		for (reg int i=1;i<=m;i++)
		{
			int sum=find(1,ask[i].l,ask[i].r);
			if (!ask[i].opt)
			{
				change(1,ask[i].l,ask[i].r-sum,0);
				change(1,ask[i].r-sum+1,ask[i].r,1);
			}
			else
			{
				change(1,ask[i].l,ask[i].l+sum-1,1);
				change(1,ask[i].l+sum,ask[i].r,0);
			}
		}
		if (find(1,q,q)==1) l=mid+1;
			else r=mid-1;
	}
	printf("%d\n",l-1);
	return 0;
}
posted @ 2019-08-10 18:47  全OI最菜  阅读(118)  评论(0编辑  收藏  举报