1. 程式人生 > 其它 >[國家集訓隊]middle——中位數思維題

[國家集訓隊]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;
}