【BZOJ2653】middle-二分答案+主席树

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Maxwei_wzj/article/details/83146910

测试地址:middle
做法: 本题需要用到二分答案+主席树。
神仙题。很久以前Mychael大佬称这题和TJOI那个排序差不多,于是二分答案那一步想得特别顺畅,结果却卡在简单主席树…菜爆,估计退役稳了。
首先看到这个题和数的大小有很大关联,想到二分答案,那么现在要解决的问题就是,如何判断最大的中位数和 m i d mid 的大小关系?
我们发现,若存在一个满足题目要求的区间,使得区间中 m i d \ge mid 的数的个数,大于等于区间中 < m i d <mid 的数的个数,那么显然这个区间中的中位数就 m i d \ge mid ,那么最大中位数肯定也 m i d \ge mid 。反之,如果不存在这样的区间,那么就不存在中位数 m i d \ge mid ,所以最大中位数就 < m i d <mid
这时候我们就想,贪心地选一个 m i d \ge mid 的数的数量 - < m i d < mid 的数的数量最大的区间进行判断就可以了,因此我们把 m i d \ge mid 的数标作 1 1 ,把 < m i d <mid 的数标作 1 -1 ,现在我们就是要找到满足要求的子段的最大子段和。注意到 [ b + 1 , c 1 ] [b+1,c-1] 中的数无论如何都会被选到,因此只要对 [ a , b ] [a,b] 求最大后缀子段和,对 [ c , d ] [c,d] 求最大前缀子段和,再加起来即可,显然可以用线段树维护。
上面我们似乎已经非常完美地解决了这个问题,可惜如果每次都这样做的话,一次询问时间复杂度就为 O ( n log 2 n ) O(n\log^2 n) ,难以接受(我自己想的时候就卡在这个位置了)。然而注意到,随着 m i d mid 的增大, 1 1 会按照原数从小到大的顺序变成 1 -1 ,且只会变一次,于是我们想到预处理出所有可能时刻的线段树,因为变化只有可能是单点修改(一个 1 1 变成 1 -1 ),用主席树存储即可,这样一来在每次询问时,就只需要二分出应该在哪棵线段树上走即可,那么我们就做到了 O ( q log 2 n ) O(q\log^2 n) 的总时间复杂度,可以通过此题。
以下是本人代码:

#include <bits/stdc++.h>
using namespace std;
const int inf=2000000000;
int n,q,a[20010],tot=0;
struct forsort
{
	int id,val;
}f[20010];
int ch[600010][2]={0},rt[20010];
int mxl[600010],mxr[600010],sum[600010];
int ans,xsum;

void pushup(int v)
{
	sum[v]=sum[ch[v][0]]+sum[ch[v][1]];
	mxl[v]=max(mxl[ch[v][0]],mxl[ch[v][1]]+sum[ch[v][0]]);
	mxr[v]=max(mxr[ch[v][1]],mxr[ch[v][0]]+sum[ch[v][1]]);
}

void buildtree(int &v,int l,int r)
{
	v=++tot;
	if (l==r)
	{
		sum[v]=1;
		mxl[v]=mxr[v]=1;
		return;
	}
	int mid=(l+r)>>1;
	buildtree(ch[v][0],l,mid);
	buildtree(ch[v][1],mid+1,r);
	pushup(v);
}

void modify(int &v,int last,int l,int r,int x)
{
	v=++tot;
	ch[v][0]=ch[last][0];
	ch[v][1]=ch[last][1];
	if (l==r)
	{
		sum[v]=-1;
		mxl[v]=mxr[v]=-1;
		return;
	}
	int mid=(l+r)>>1;
	if (x<=mid) modify(ch[v][0],ch[last][0],l,mid,x);
	else modify(ch[v][1],ch[last][1],mid+1,r,x);
	pushup(v);
}

void updateans(int v,bool type)
{
	if (!type) ans=max(ans,mxr[v]+xsum);
	else ans=max(ans,mxl[v]+xsum);
	xsum+=sum[v];
}

void query(int v,int l,int r,int s,int t,bool type)
{
	if (l>=s&&r<=t)
	{
		updateans(v,type);
		return;
	}
	int mid=(l+r)>>1;
	if (type)
	{
		if (s<=mid) query(ch[v][0],l,mid,s,t,type);
		if (t>mid) query(ch[v][1],mid+1,r,s,t,type);
	}
	else
	{
		if (t>mid) query(ch[v][1],mid+1,r,s,t,type);
		if (s<=mid) query(ch[v][0],l,mid,s,t,type);
	}
}

bool check(int a,int b,int c,int d,int x)
{
	int l=0,r=n;
	while(l<r)
	{
		int mid=(l+r)>>1;
		if (f[mid+1].val>=x) r=mid;
		else l=mid+1;
	}
	
	int totans=0;
	if (b<c-1)
	{
		ans=-inf,xsum=0;
		query(rt[l],0,n-1,b+1,c-1,0);
		totans+=xsum;
	}
	ans=-inf,xsum=0;
	query(rt[l],0,n-1,a,b,0);
	totans+=ans;
	ans=-inf,xsum=0;
	query(rt[l],0,n-1,c,d,1);
	totans+=ans;
	
	return totans>=0;
}

bool cmp(forsort a,forsort b)
{
	return a.val<b.val;
}

int main()
{
	int l=0,r=0;
	
	scanf("%d",&n);
	for(int i=0;i<n;i++)
	{
		scanf("%d",&a[i]);
		r=max(r,a[i]);
		f[i+1].id=i,f[i+1].val=a[i];
	}
	
	sort(f+1,f+n+1,cmp);
	buildtree(rt[0],0,n-1);
	for(int i=1;i<=n;i++)
		modify(rt[i],rt[i-1],0,n-1,f[i].id);
	
	scanf("%d",&q);
	int lastans=0;
	for(int i=1;i<=q;i++)
	{
		int op[5];
		for(int j=0;j<4;j++)
		{
			scanf("%d",&op[j]);
			op[j]=(op[j]+lastans)%n;
		}
		sort(op,op+4);
		int L=l,R=r;
		while(L<R)
		{
			int mid=(L+R)>>1;
			if (check(op[0],op[1],op[2],op[3],mid+1))
				L=mid+1;
			else R=mid;
		}
		printf("%d\n",lastans=L);
	}
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Maxwei_wzj/article/details/83146910