[国家集训队]middle

题目描述

一个长度为n的序列a,设其排过序之后为b,其中位数定义为b[n/2],其中a,b从0开始标号,除法取下整。

给你一个长度为n的序列s。

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

其中a<b<c<d。

位置也从0开始标号。

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

输入输出格式

输入格式:

第一行序列长度n。

接下来n行按顺序给出a中的数。

接下来一行Q。

然后Q行每行a,b,c,d,我们令上个询问的答案是x(如果这是第一个询问则x=0)。

令数组q={(a+x)%n,(b+x)%n,(c+x)%n,(d+x)%n}。

将q从小到大排序之后,令真正的要询问的a=q[0],b=q[1],c=q[2],d=q[3]。

输入保证满足条件。

输出格式:

Q行依次给出询问的答案。

输入输出样例

输入样例#1: 复制

5
170337785
271451044
22430280
969056313
206452321
3
3 1 0 2
2 3 1 4
3 1 4 0

输出样例#1: 复制

271451044
271451044
969056313

说明

0:n,Q<=100

1,...,5:n<=2000

0,...,19:n<=20000,Q<=25000


题解

可以将中位数问题转化成类似于01序列排序的问题
每次二分一个中位数
然后原序列大于等于这个数的设为1
其余的设为-1
然后就要找一段区间,使区间左端点在[a,b],右端点在[c,d]
如果有这样一段区间的值>=0,那么二分的这个数就是合法的
然后考虑优化这个过程
显然答案可以分成三部分
一部分是[b+1,c-1]的必须选择的部分
然后再维护一个[a,b]的最大后缀和和一个[c,d]的最大前缀和
这个操作我们可以用主席树来实现
对于每一个权值建一棵主席树
维护区间和,最大后缀和和最大前缀和

题解

#include<map>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
# define ls t[now].l
# define rs t[now].r
const int M = 25005 ;
using namespace std ;
inline int read() {
	char c = getchar() ; int x = 0 , w = 1 ;
	while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
	while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
	return x*w ;
}

int n , v[M] , s[M] , rt[M];
int tot , cnt , val[M] , q[8] ;
map < int , int > p ;
vector < int > vec[M] ;
struct Node {
	int l , r , Lmax , Rmax , Sum ;
	inline Node () {  }
} t[M << 5] ;
inline void Merge(Node &temp , Node tl , Node tr) {
	temp.Sum = tl.Sum + tr.Sum ;
	temp.Lmax = max(tl.Lmax , tl.Sum + tr.Lmax) ;
	temp.Rmax = max(tr.Rmax , tr.Sum + tl.Rmax) ;
}
void Insert(int x , int v , int l , int r , int &now) {
	t[++tot] = t[now] ; now = tot ;
	if(l == r) {
		t[now].Lmax = t[now].Rmax = t[now].Sum = v ;
		return ;
	}
	int mid = (l + r) >> 1 ;
	if(mid >= x) Insert(x , v , l , mid , ls) ;
	else Insert(x , v , mid + 1 , r , rs) ;
	Merge(t[now] , t[ls] , t[rs]) ;
}
Node query(int L , int R , int l , int r , int now) {
	if(l == L && r == R) return t[now] ;
	int mid = (l + r) >> 1 ;
	if(mid >= R) return query(L , R , l , mid , ls) ;
	else if(mid < L) return query(L , R , mid + 1 , r , rs) ;
	else {
		Node temp , tl , tr ;
		tl = query(L , mid , l , mid , ls) ;
		tr = query(mid + 1 , R , mid + 1  , r , rs) ;
		Merge(temp , tl , tr) ;
		return temp ;
	}
}
inline bool chk(int mid) {
	int Ans = 0 , tmid = 0 , tl = 0 , tr = 0 ;
	if(q[2] + 1 <= q[3] - 1) tmid = query(q[2] + 1 , q[3] - 1 , 1 , n , rt[mid]).Sum ;
	tl = query(q[1] , q[2] , 1 , n , rt[mid]).Rmax ;
	tr = query(q[3] , q[4] , 1 , n , rt[mid]).Lmax ;
	Ans = tmid + tl + tr ;
	if(q[2] == q[3]) {
		if(v[q[2]] > mid) Ans ++ ;
		else Ans -- ;
	}
	return (Ans >= 0) ;
}
inline int Solve() {
	int l = 1 , r = cnt , ret = 0 ;
	while(l <= r) {
		int mid = (l + r) >> 1 ;
		if(chk(mid)) l = mid + 1 , ret = mid ;
		else r = mid - 1 ;
	}
	return val[ret] ;
}
int main() {
	n = read() ;
	for(int i = 1 ; i <= n ; i ++) {
		v[i] = read() ;
		s[i] = v[i] ;
	}
	sort(s + 1 , s + n + 1) ;
	for(int i = 1 ; i <= n ; i ++) {
		if(i == 1 || s[i] != s[i - 1]) ++ cnt ;
		p[s[i]] = cnt ; val[cnt] = s[i] ;
	}
	for(int i = 1 ; i <= n ; i ++) {
		v[i] = p[v[i]] ;
		vec[v[i]].push_back(i) ;
	}	
	for(int i = 1 ; i <= n ; i ++) Insert(i , 1 , 1 , n , rt[0]) ;
	for(int i = 1 ; i <= cnt ; i ++) {
		rt[i] = rt[i - 1] ;
		for(int j = 0 , x ; j < vec[i - 1].size() ; j ++) {
			x = vec[i - 1][j] ;
			Insert(x , -1 , 1 , n , rt[i]) ;
		}
	}
	int T = read() , LastAns = 0 ;
	while(T --) {
		for(int i = 1 ; i <= 4 ; i ++)
			q[i] = (read() + LastAns) % n + 1 ;
		sort(q + 1 , q + 5) ;
		LastAns = Solve() ;
		printf("%d\n",LastAns) ;
	}
	return 0 ;
}
posted @ 2018-11-24 21:08  beretty  阅读(181)  评论(0编辑  收藏  举报