[国家集训队]middle

[国家集训队]middle

题解

如果离线的话是一道很水的题,但它要求强制在线,但还是很水。

按它的定义来说,中位数是满足在本区间中,大于等于其的数比小于其的数更多的数中最大的一个数。它要求的又是要在所有可以选择的区间中使得其中位数最大的一个,我们就很容易地想到了二分。
我们可以先二分中位数是什么,由于中位数本身是不具有单调性的,我们必须要想个法子使其具有单调性,我们发现,如果就看大于它的数比小于它数的差距,明显是具有单调性的,于是我们就可以去二分了。

由于我们要它的区间不是固定的,我们需找到使其差值最大的一个区间。
我们假设我们最优的区间为 [ l , r ] [l,r] [l,r],一定满足 l ∈ [ l 1 , l 2 ] , r ∈ [ r 1 , r 2 ] l\in [l1,l2],r\in[r1,r2] l[l1,l2],r[r1,r2]。区间 [ l , r ] [l,r] [l,r]可以被拆成 [ l , l 2 ] , ( l 2 , r 1 ) , [ r 1 , r ] [l,l2],(l2,r1),[r1,r] [l,l2],(l2,r1),[r1,r]三个独立的区间。其中 ( l 2 , r 1 ) (l2,r1) (l2,r1)是固定的,可以先算出来,接下来我们只需要最出 l , r l,r l,r使得 [ l , l 2 ] [l,l2] [l,l2] [ r 1 , r ] [r1,r] [r1,r]的值最大即可,这个操作可以通过线段树来实现。

我们只需要对每个值建出一棵 0 / 1 0/1 0/1的线段树,在上面找出 [ l , l 2 ] [l,l2] [l,l2] [ r 1 , r ] [r1,r] [r1,r]的最大前缀与后缀。但这样做明显会使得空间炸掉,考虑可持久化。我们发现如果我们依值的顺序从小到大建树,值只会在一个位置从1变成-1,所以可持久化后的空间复杂度为 O ( n l o g   n ) O\left(nlog\,n\right) O(nlogn),可以承受。

总时间复杂度 O ( q l o g 2   n ) O\left(qlog^2 \,n\right) O(qlog2n),可以过。


update in 2021.9.19:
事实上跟本不需要同时维护前缀后缀,只维护前缀,将 [ r 1 , r 2 ] [r1,r2] [r1,r2]中最大前缀与 [ l 1 − 1 , l 2 − 1 ] [l1-1,l2-1] [l11,l21]中最小前缀差分即可。
顺便换上一份跑得更快的代码。

源码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 20005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;       
const int INF=0x3f3f3f3f;       
const int mo=1e9+7;
const int inv2=499122177;
const int jzm=2333;
const int lim=1e9;
const int n1=400;
const int zero=10000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-5;
typedef pair<int,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1LL)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1LL;}return t;}
int n,q,a[MAXN],root[MAXN],ord[MAXN],li[5];
struct ming{int lson,rson,maxx,minn,sum;ming(){sum=maxx=minn=lson=rson=0;}};
class SegmentTree{
	private:
		ming tr[MAXN*30];int tot;
		void pushup(int rt){
			tr[rt].sum=tr[tr[rt].lson].sum+tr[tr[rt].rson].sum;
			tr[rt].minn=min(tr[tr[rt].lson].minn,tr[tr[rt].lson].sum+tr[tr[rt].rson].minn);
			tr[rt].maxx=max(tr[tr[rt].lson].maxx,tr[tr[rt].lson].sum+tr[tr[rt].rson].maxx);
		}
	public: 
		void insert(int &now,int las,int l,int r,int ai){
			if(l>r||l>ai||r<ai)return ;int mid=l+r>>1;tr[now=++tot]=tr[las];
			if(l==r){tr[now].maxx=tr[now].minn=tr[now].sum=-1;return ;}
			if(ai<=mid)insert(tr[now].lson,tr[las].lson,l,mid,ai);
			if(ai>mid)insert(tr[now].rson,tr[las].rson,mid+1,r,ai);
			pushup(now);
		}
		int queryMin(int rt,int l,int r,int al,int ar){
			if(l>r||l>ar||r<al||al>ar)return INF;
			if(al<=l&&r<=ar)return tr[rt].minn;int mid=l+r>>1,res=INF;
			if(al<=mid)res=min(res,queryMin(tr[rt].lson,l,mid,al,ar));
			if(ar>mid)res=min(res,tr[tr[rt].lson].sum+queryMin(tr[rt].rson,mid+1,r,al,ar));
			return res;
		}
		int queryMax(int rt,int l,int r,int al,int ar){
			if(l>r||l>ar||r<al||al>ar)return -INF;
			if(al<=l&&r<=ar)return tr[rt].maxx;int mid=l+r>>1,res=-INF;
			if(al<=mid)res=max(res,queryMax(tr[rt].lson,l,mid,al,ar));
			if(ar>mid)res=max(res,tr[tr[rt].lson].sum+queryMax(tr[rt].rson,mid+1,r,al,ar));
			return res;
		}
		void build(int &rt,int l,int r){
			rt=++tot;if(l==r){tr[rt].minn=tr[rt].maxx=tr[rt].sum=1;return ;}
			int mid=l+r>>1;build(tr[rt].lson,l,mid);build(tr[rt].rson,mid+1,r);pushup(rt);
		}
}T;
bool cmp(int x,int y){return a[x]<a[y];}
signed main(){
	read(n);for(int i=1;i<=n;i++)read(a[i]),ord[i]=i;read(q);
	sort(ord+1,ord+n+1,cmp);T.build(root[0],1,n);int lastans=0;
	for(int i=1;i<=n;i++)T.insert(root[i],root[i-1],1,n,ord[i]);
	for(int i=1;i<=q;i++){
		for(int j=1;j<=4;j++)read(li[j]),li[j]=(li[j]%n+lastans)%n+1;
		sort(li+1,li+5);int l=1,r=n;
		while(l<r){
			int mid=l+r+1>>1;
			int tmpl=T.queryMin(root[mid-1],1,n,max(1,li[1]-1),li[2]-1);
			int tmpr=T.queryMax(root[mid-1],1,n,li[3],li[4]);if(li[1]==1)tmpl=min(tmpl,0);
			if(tmpr-tmpl>=0)l=mid;else r=mid-1;
		}
		printf("%d\n",lastans=a[ord[l]]);
	}
	return 0;
}

谢谢!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值