【洛谷P2839】middle

题目

题目链接:https://www.luogu.com.cn/problem/P2839
一个长度为 \(n\) 的序列 \(a\),设其排过序之后为 \(b\),其中位数定义为 \(b_{n/2}\),其中 \(a,b\)\(0\) 开始标号,除法取下整。

给你一个长度为 \(n\) 的序列 \(s\)

回答 \(Q\) 个这样的询问:\(s\) 的左端点在 \([a,b]\) 之间,右端点在 \([c,d]\) 之间的子区间中,最大的中位数。

其中 \(a<b<c<d\)

位置也从 \(0\) 开始标号。

我会使用一些方式强制你在线。

\(n \leq 20000\)\(Q \leq 25000\)

思路

很有趣的一道题。

求区间 \([l,r]\) 的中位数,可以二分答案 \(mid\),将大于等于 \(mid\) 的数字全部设为 \(1\),小于 \(mid\) 的数字全部设为 \(-1\),如果区间 \([l,r]\) 的和大于 \(0\),那么答案就不小于 \(mid\),否则答案小于 \(mid\)

本题求左端点在 \([l_1,r_1]\),右端点在 \([l_2,r_2]\) 中最大的中位数,发现 \([r_2,l_1]\) 这一段是必须取的,然后前后各可以取一段前缀 / 后缀。

依然二分答案 \(mid\),转化为 \(1,-1\) 序列后,我们需要最大化左右两端前后缀的和使得总和尽量大,如果这个最大的和大于 \(0\),那么答案就超过 \(mid\),反之答案小于 \(mid\)

那么可以先离散化,建立 \(n\) 棵线段树,第 \(i\) 棵线段树的区间 \([l,r]\) 表示当 \(mid=i\) 时,区间 \([l,r]\) 的和。接下来维护区间和、区间最大前缀 / 后缀和即可。

但是这样空间是 \(O(n^2)\) 的,发现第 \(i\) 棵线段树与第 \(i-1\) 棵线段树唯一有区别的地方就是离散化后数值为 \(i-1\) 的位置,总共的修改次数不会超过 \(O(n)\),所以用主席树即可。

时间复杂度 \(O((n+m)\log^2 n)\)

代码

#include <bits/stdc++.h>
using namespace std;

const int N=20010,LG=15,MAXN=N*LG*4;
int n,Q,tot,last,a[N],b[N],rt[N];
vector<int> pos[N];

struct SegTree
{
	int tot,lc[MAXN],rc[MAXN],sum[MAXN],lmax[MAXN],rmax[MAXN];
	
	void pushup(int x)
	{
		sum[x]=sum[lc[x]]+sum[rc[x]];
		lmax[x]=max(lmax[lc[x]],sum[lc[x]]+lmax[rc[x]]);
		rmax[x]=max(rmax[rc[x]],sum[rc[x]]+rmax[lc[x]]);
	}
	
	int build(int l,int r)
	{
		int x=++tot;
		sum[x]=lmax[x]=rmax[x]=r-l+1;
		if (l==r) return x;
		int mid=(l+r)>>1;
		lc[x]=build(l,mid);
		rc[x]=build(mid+1,r);
		return x;
	}
	
	int update(int x,int now,int l,int r,int k)
	{
		if (!x || x==now)
		{
			x=++tot;
			lc[x]=lc[now]; rc[x]=rc[now];
			sum[x]=sum[now]; lmax[x]=lmax[now]; rmax[x]=rmax[now];
		}
		if (l==k && r==k)
		{
			sum[x]=-1; lmax[x]=rmax[x]=0;
			return x;
		}
		int mid=(l+r)>>1;
		if (k<=mid) lc[x]=update(lc[x],lc[now],l,mid,k);
			else rc[x]=update(rc[x],rc[now],mid+1,r,k);
		pushup(x);
		return x;
	}
	
	int query1(int x,int l,int r,int ql,int qr)
	{
		if (l==ql && r==qr) return sum[x];
		int mid=(l+r)>>1;
		if (qr<=mid) return query1(lc[x],l,mid,ql,qr);
		if (ql>mid) return query1(rc[x],mid+1,r,ql,qr);
		return query1(lc[x],l,mid,ql,mid)+query1(rc[x],mid+1,r,mid+1,qr);
	}
	
	int query2(int x,int l,int r,int ql,int qr)
	{
		if (l==ql && r==qr) return rmax[x];
		int mid=(l+r)>>1;
		if (qr<=mid) return query2(lc[x],l,mid,ql,qr);
		if (ql>mid) return query2(rc[x],mid+1,r,ql,qr);
		int sr=query1(rc[x],mid+1,r,mid+1,qr);
		int mr=query2(rc[x],mid+1,r,mid+1,qr);
		int ml=query2(lc[x],l,mid,ql,mid);
		return max(mr,sr+ml);
	}
	
	int query3(int x,int l,int r,int ql,int qr)
	{
		if (l==ql && r==qr) return lmax[x];
		int mid=(l+r)>>1;
		if (qr<=mid) return query3(lc[x],l,mid,ql,qr);
		if (ql>mid) return query3(rc[x],mid+1,r,ql,qr);
		int sl=query1(lc[x],l,mid,ql,mid);
		int ml=query3(lc[x],l,mid,ql,mid);
		int mr=query3(rc[x],mid+1,r,mid+1,qr);
		return max(ml,sl+mr);
	}
}seg;

int binary(int l1,int r1,int l2,int r2)
{
	int l=1,r=tot,mid;
	while (l<=r)
	{
		mid=(l+r)>>1;
		if (seg.query1(rt[mid],1,n,r1,l2)+seg.query2(rt[mid],1,n,l1,r1-1)+seg.query3(rt[mid],1,n,l2+1,r2)>=0) l=mid+1;
			else r=mid-1;
	}
	return l-1;
}

int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		b[i]=a[i];
	}
	sort(b+1,b+1+n);
	tot=unique(b+1,b+1+n)-b-1;
	for (int i=1;i<=n;i++)
	{
		a[i]=lower_bound(b+1,b+1+tot,a[i])-b;
		pos[a[i]].push_back(i);
	}
	tot++;
	rt[0]=seg.build(1,n);
	for (int i=1;i<=tot;i++)
	{
		rt[i]=rt[i-1];
		for (int j=0;j<pos[i-1].size();j++)
			rt[i]=seg.update(rt[i],rt[i-1],1,n,pos[i-1][j]);
	}
	scanf("%d",&Q);
	while (Q--)
	{
		int p[5];
		scanf("%d%d%d%d",&p[1],&p[2],&p[3],&p[4]);
		p[1]=(p[1]+last)%n+1; p[2]=(p[2]+last)%n+1;
		p[3]=(p[3]+last)%n+1; p[4]=(p[4]+last)%n+1;
		sort(p+1,p+5);
		printf("%d\n",last=b[binary(p[1],p[2],p[3],p[4])]);
	}
	return 0;
}
posted @ 2020-11-19 21:09  stoorz  阅读(97)  评论(0编辑  收藏  举报