题意:给一个序列,强制在线,每次查询左端点在[a,b]之间,右端点在[c,d]之间的所有子列中的中位数的最大值。。
链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2653
解法:考虑这样一个问题,查询一个子区间[l, r]的中位数是否 >= x,可以这样做:开一个b数组,对于原数列a,如果ai < x,则 bi = -1,否则,bi = 1;那么,只需要查询b[l..r]的和,是否>=0,就可以判断中位数是否>=x了,是吧?是的。然后,这题用这个思想来做。
将a数列离散化,假设离散化后,最大数为ntem,最小当然为1,则从1到ntem都建一个棵线段树,线段树上就是上面描述的b数组,并且同时维护子区间和,子区间前缀和最大值,子区间后缀和最大值。。这样子的话,我们每次查询,二分中位数x,下面判断最大中位数是否可以 >= x:已知[a,b], [c,d],只需要查询[b + 1, c - 1]的子区间和t2,[a, b]区间的后缀和最大值t1,[c, d]区间的前缀和最大值t3,则令t = t1 + t2 + t3,如果t >=0,则说明最大中位数可以>=x,否则不行,以此来决定二分走的方向,最后就可以得到答案。。
好题啊!!
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<iostream>
#include<cstring>
using namespace std;
#define N 20010
int a[N], tem[N], ntem;
struct Edge{
int to, next;
}e[N];
int head[N], ne;
void add(int a, int b){
int t = ++ne;
e[t].to = b;
e[t].next = head[a];
head[a] = t;
}
#define MAXN N * (4 + 15)
int lch[MAXN], rch[MAXN], sum[MAXN], lmax[MAXN], rmax[MAXN], Tid[N], nnod;
void pullup(int id){
sum[id] = sum[lch[id]] + sum[rch[id]];
lmax[id] = max(lmax[lch[id]], sum[lch[id]] + lmax[rch[id]]);
rmax[id] = max(rmax[lch[id]] + sum[rch[id]], rmax[rch[id]]);
}
int plant(int l,int r){
int t = ++nnod;
if(l == r){
sum[t] = lmax[t] = rmax[t] = 1;
return t;
}
int mid = (l + r) >> 1;
lch[t] = plant(l, mid);
rch[t] = plant(mid + 1, r);
pullup(t);
return t;
}
int update(int id, int p, int l, int r){
int t = ++nnod;
lch[t] = lch[id], rch[t] = rch[id];
if(l == r){
sum[t] = lmax[t] = rmax[t] = -1;
return t;
}
int mid = (l + r) >> 1;
if(p <= mid) lch[t] = update(lch[t], p, l, mid);
else rch[t] = update(rch[t], p, mid + 1, r);
pullup(t);
return t;
}
void querymax(int id, int ql, int qr, int l, int r, int &s, int &lm, int &rm){
if(ql == l && qr == r){
s = sum[id], lm = lmax[id], rm = rmax[id];
return;
}
int mid = (l + r) >> 1;
if(qr <= mid) querymax(lch[id], ql, qr, l, mid, s, lm, rm);
else if(mid < ql) querymax(rch[id], ql, qr, mid + 1, r, s, lm, rm);
else{
int s1, lm1, rm1;
querymax(lch[id], ql, mid, l, mid, s1, lm1, rm1);
int s2, lm2, rm2;
querymax(rch[id], mid + 1, qr, mid + 1, r, s2, lm2, rm2);
s = s1 + s2;
lm = max(lm1, s1 + lm2);
rm = max(rm1 + s2, rm2);
}
}
int main(){
int n;
scanf("%d",&n);
for(int i = 1; i <= n; i++){
scanf("%d", a + i);
tem[i] = a[i];
}
ntem = n;
sort(tem + 1, tem + 1 + ntem);
ntem = (int)(unique(tem + 1, tem + 1 + ntem) - (tem + 1));
for(int i = 0; i <= ntem; i++) head[i] = -1;
ne = 0;
for(int i = 1; i <= n; i++){
a[i] = (int)(lower_bound(tem + 1, tem + 1 + ntem, a[i]) - tem);
add(a[i], i);
}
Tid[0] = plant(1, n);
for(int i = 1; i <= ntem; i++){
Tid[i] = Tid[i-1];
for(int j = head[i - 1]; j != -1; j = e[j].next){//注意是head[i-1]而不是head[i]
int to = e[j].to;
Tid[i] = update(Tid[i], to, 1, n);
}
}
int q, x = 0;
scanf("%d",&q);
for(int iq = 1; iq <= q; iq++){
int a[10];
for(int i = 0; i < 4; i++){
scanf("%d",a + i);
a[i] += x;
if(a[i] >= n) a[i] %= n;
a[i] += 1;
}
sort(a, a + 4);
int l = 1, r = ntem, mid;
while(l < r){
mid = (l + r) >> 1;
mid++;
int s, lmax, rmax, t1, t2, t3;
querymax(Tid[mid], a[0], a[1], 1, n, s, lmax, rmax);
t1 = rmax;
querymax(Tid[mid], a[2], a[3], 1, n, s, lmax, rmax);
t3 = lmax;
if(a[1] + 1 <= a[2] - 1){
querymax(Tid[mid], a[1] + 1, a[2] - 1, 1, n, s, lmax, rmax);
t2 = s;
}
else t2 = 0;
int t = t1 + t2 + t3;
if(t >= 0) l = mid;
else r = mid - 1;
}
printf("%d\n", tem[l]);
x = tem[l] % n;
}
return 0;
}