BZOJ2653 middle 中位数套路 可持久化线段树优化

BZOJ2653 middle 中位数套路 可持久化线段树优化

题意

给定长度为\(n\)的序列,给定\(q\)个询问,每个询问将给定区间\([a,b]\),\([c,d]\),要求左端点和右端点分别位于两个区间中,求区间的最大中位数能取多少,强制在线

\[n \leq 2e4\\ q \leq 2500 \]

分析

对于中位数通常可以二分答案,然后将原序列大于等于该数的记作\(1\),小于该数的记作\(-1\),如果和大于等于\(0\),说明中位数可以变大

此题区间端点可以任意选择,但是中间\([b + 1,c - 1]\)必须选定,因此一定选择\([a,b]\)的最大后缀和和\([c,d]\)的最大前缀和累计

因此不妨对每个数建立线段树,维护区间最大前缀,后缀,区间和。对于每个二分的数考察其线段树,可以可持久化的原因是每次增加一个数最多需要更改一个位置,空间足够。

感觉是一种比较巧妙的建树思想

代码

#include<bits/stdc++.h>
#define re register
#define pii pair<int,int>
#define fi first
#define se second 
using namespace std;
typedef long long ll;

const int maxn = 2e4 + 5;

ll rd(){
	ll x = 0;
	int f = 1;
	char ch = getchar();
	while(ch < '0' || ch > '9') {
		if(ch == '-') f = -1;
		ch = getchar();
	} 
	while(ch >= '0' && ch <= '9') {
		x = x * 10 + ch - '0';
		ch = getchar();
	}
	return x * f;
}


struct info{
	int tot,lmax,rmax;
	inline info operator + (const info &a) {
		return (info){
			tot + a.tot,
			max(lmax,tot + a.lmax),
			max(a.rmax,a.tot + rmax)
		};
	}
	inline void init(int _tot,int _l,int _r){
		tot = _tot;
		lmax = _l;
		rmax = _r;
	}
};

struct Node{
	int ls,rs;
	info sum;
}node[maxn * 40 + 5];

int rt[maxn];
pii a[maxn];
int n;

struct PST{
	int tt;
	inline void push_up(int i){
		node[i].sum = node[node[i].ls].sum + node[node[i].rs].sum;
	}
	void build(int &rt,int l,int r){
		int tmp = rt;
		rt = ++tt;
		node[rt] = node[tmp];
		if(l == r) {
			node[rt].sum.init(1,1,1);
			return;
		}
		int mid = l + r >> 1;
		build(node[rt].ls,l,mid);
		build(node[rt].rs,mid + 1,r);
		push_up(rt);
	}
	void update(int &rt,int l,int r,int v){
		int tmp = rt;
		rt = ++tt;
		node[rt] = node[tmp];
		if(l == r) {
			node[rt].sum.init(-1,-1,-1);
			return;
		}
		int mid = l + r >> 1;
		if(v <= mid) update(node[rt].ls,l,mid,v);
		else update(node[rt].rs,mid + 1,r,v);
		push_up(rt);
	}
	info query(int i,int l,int r,int L,int R) {
		if(l >= L && r <= R) return node[i].sum;
		int mid = l + r >> 1;
		if(mid >= R) return query(node[i].ls,l,mid,L,R);
		else if(L > mid) return query(node[i].rs,mid + 1,r,L,R); 
		return query(node[i].ls,l,mid,L,mid) + query(node[i].rs,mid + 1,r,mid + 1,R);
	}
}pst;

bool check(int i,int a,int b,int c,int d){
	int res = 0;
	if(b + 1 <= c - 1) res += pst.query(rt[i],1,n,b + 1,c - 1).tot;
	res += pst.query(rt[i],1,n,a,b).rmax;
	res += pst.query(rt[i],1,n,c,d).lmax;
	return res >= 0;
} 




int main(){
	n = rd();
	for(int i = 1;i <= n;i++){
		a[i].fi = rd();
		a[i].se = i;
	}
	sort(a + 1,a + n + 1);
	pst.build(rt[1],1,n);
	for(int i = 2;i <= n;i++){
		rt[i] = rt[i - 1];
		pst.update(rt[i],1,n,a[i - 1].se);
	}
	int m = rd();
	int last = 0;
	while(m--){
		int c[4];
		for(int i = 0;i < 4;i++)
			c[i] = rd(),c[i] = (c[i] + last) % n + 1;
		sort(c,c + 4);
		int l = 1,r = n;
		while(l < r) {
			int mid = l + r + 1 >> 1;
			if(check(mid,c[0],c[1],c[2],c[3]))
				l = mid;
			else r = mid - 1;
		}
		last = a[l].fi;
		printf("%d\n",last);
	}
}
上一篇:IDEA如何在包下面继续建包


下一篇:最大子序和:暴力->递归->动规->线段树