BZOJ 2653 middle(二分+主席树)

题意:给一个序列,强制在线,每次查询左端点在[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;
}

你可能感兴趣的:(BZOJ 2653 middle(二分+主席树))