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);
}
}