[国家集训队]middle——中位数思维题

传送门

P2839 [国家集训队]middle

题解

遇到求中位数,一般套路性的方法是二分答案,然后求出该数在区间内的排名,

但是此题稍微有点区别,区间大小不确定,所以我们用到一个更万能的解决中位数问题的套路方法,

设<mid的数为-1,≥mid的数为1,得到b数组,那么若该区间的和S≥0,则该区间中位数≥mid,若S<0,则该区间中位数<mid;

对于l\epsilon [a,b],r\epsilon [c,d]的区间,首先区间[b+1,c-1]内的值肯定得加上,然后,要让中位数最大化,也就是让S尽可能大,

所以还要加上[a,b]内的最大后缀和[c,d]内的最大前缀,然后判断S正负;

区间和、最大前缀后缀可以用线段树维护,这个简单;

那么b数组怎么求呢?把a数组离散化后,总共有不超过n个mid,对于每个mid都有个不同的b数组,

建n棵线段树?时间空间都过不去;

考虑从b(mid)b(mid+1),可以发现只有排名为mid的数会从1变为-1,所以可以用主席树求出每一棵树,复杂度O(nlogn)

查询单次复杂度O(log^2n),所以总复杂度O(nlog^2n)

并不需要写3种线段树,因为线段树内维护最大前缀后缀时要用到区间和,所以你可以用一棵线段树维护三个值,方便调用;

查询函数也不需要分别写3个,只需一个函数返回3个值,复杂度都一样,并且这题常数要求并不苛刻(5625000*常数),常数大一点随便过。

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<map>
#define ll long long
#define MAXN 20005
using namespace std;
inline ll read(){
	ll x=0;bool f=1;char s=getchar();
	while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
	while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+s-'0',s=getchar();
	return f?x:-x;
}
int n,q,IN,root[MAXN],in[5],id;
ll a[MAXN],b[MAXN],ans;
vector<int>c[MAXN];
struct itn{
	int ls,rs,s,pr,su;
	itn(){}
	itn(int L,int R,int S,int P,int U){
		ls=L,rs=R,s=S,pr=P,su=U;
	}
}t[MAXN*20];
map<ll,int>mp;
inline void build(int x,int l,int r){
	if(l==r){t[x].s=t[x].pr=t[x].su=1;return;}
	int mid=(l+r)>>1;
	t[x].ls=++IN,t[x].rs=++IN;
	build(t[x].ls,l,mid),build(t[x].rs,mid+1,r);
	t[x].s=t[t[x].ls].s+t[t[x].rs].s;
	t[x].pr=max(t[t[x].ls].pr,t[t[x].ls].s+t[t[x].rs].pr);
	t[x].su=max(t[t[x].rs].su,t[t[x].rs].s+t[t[x].ls].su);
}
inline void add(int x,int y,int l,int r,int a){
	if(l==r){t[x].s=t[x].pr=t[x].su=-1;return;}
	int mid=(l+r)>>1;
	if(a<=mid)t[x].ls=++IN,t[x].rs=t[y].rs,add(t[x].ls,t[y].ls,l,mid,a);
	else t[x].ls=t[y].ls,t[x].rs=++IN,add(t[x].rs,t[y].rs,mid+1,r,a);
	t[x].s=t[t[x].ls].s+t[t[x].rs].s;
	t[x].pr=max(t[t[x].ls].pr,t[t[x].ls].s+t[t[x].rs].pr);
	t[x].su=max(t[t[x].rs].su,t[t[x].rs].s+t[t[x].ls].su);
}
inline itn query(int x,int l,int r,int a,int b){
	if(a>b)return itn(a,b,0,0,0);
	if(l==a&&r==b)return itn(a,b,t[x].s,t[x].pr,t[x].su);
	int mid=(l+r)>>1;
	if(a<=mid){
		if(b>mid){
			itn u=query(t[x].ls,l,mid,a,mid),v=query(t[x].rs,mid+1,r,mid+1,b);
			return itn(a,b,u.s+v.s,max(u.pr,u.s+v.pr),max(v.su,v.s+u.su));
		}
		else return query(t[x].ls,l,mid,a,b);
	}
	else return query(t[x].rs,mid+1,r,a,b);
}
int main()
{
	n=read();
	for(int i=1;i<=n;i++)a[i]=read(),mp[a[i]];
	map<ll,int>::iterator it;
	for(it=mp.begin();it!=mp.end();it++)b[++id]=it->first,it->second=id;
	for(int i=1;i<=n;i++)a[i]=mp[a[i]],c[a[i]].push_back(i);
	root[1]=++IN,build(IN,1,n);
	for(int i=2;i<=id;i++){
		root[i]=root[i-1];
		for(int j=0;j<c[i-1].size();j++){
			int x=++IN;
			add(x,root[i],1,n,c[i-1][j]),root[i]=x;
		}
	}
	q=read();
	while(q--){
		for(int i=0;i<4;i++)in[i]=(read()+ans)%(1ll*n)+1;
		sort(in,in+4);
		int l=1;
		for(int i=15;i>=0;i--)
			if(l+(1<<i)<=id){
				int o=l+(1<<i),sum=query(root[o],1,n,in[1]+1,in[2]-1).s;
				sum+=query(root[o],1,n,in[0],in[1]).su+query(root[o],1,n,in[2],in[3]).pr;
				if(sum>-1)l=o;
			}
		printf("%lld\n",ans=b[l]);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43960287/article/details/111887553
今日推荐