[bzoj2653]middle【可持久化线段树】【二分】

【题目链接】
  https://www.lydsy.com/JudgeOnline/problem.php?id=2653
【题解】
  遇到求中位数的题,不难想到二分答案,然后把序列转化为-1和1。
  考虑二分后如何判断,对于每个不同的二分的值,可以按位置为下标建立一棵线段树,每个节点记录当前是-1还是1。 [b,c] [ b , c ] 必须全取。 [a,b) [ a , b ) , (c,d] ( c , d ] 是最大右(左)子段和。然后把所得的值与0进行比较, <0 < 0 则不合法。
  由于对于两个相邻的二分的值,线段树的叶子节点上只有一个点的值会改变。所以可以用可持久化线段树实现。
  时间复杂度: O(Nlog2N) O ( N ∗ l o g 2 N )
【代码】

/* - - - - - - - - - - - - - - -
    User :      VanishD
    problem :   [bzoj2653] 
    Points :    segment tree
- - - - - - - - - - - - - - - */
# include 
# define    ll      long long
# define    inf     0x3f3f3f3f
# define    N       100010
using namespace std;
int read(){
    int tmp = 0, fh = 1; char ch = getchar();
    while (ch < '0' || ch > '9'){ if (ch == '-') fh = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9'){ tmp = tmp * 10 + ch - '0'; ch = getchar(); }
    return tmp * fh;
}
struct Node{
    int num, id; 
}h[N];
struct Tree{
    int pl, pr, num, numl, numr; 
}T[N * 20];
int p[10], n, q, number[N], L, R, rt[N], place, now, a, b, c, d;
map <int, int> mp;
vector <int> in[N]; 
void build(int &p, int l, int r){
    p = ++place;
    T[p].numl = 0, T[p].numr = 0;
    T[p].num = -(r - l + 1);
    if (l != r){
        int mid = (l + r) / 2;
        build(T[p].pl, l, mid);
        build(T[p].pr, mid + 1, r);
    }
}
void reget(int p){
    T[p].numl = max(T[T[p].pl].num + T[T[p].pr].numl, T[T[p].pl].numl);
    T[p].numr = max(T[T[p].pr].num + T[T[p].pl].numr, T[T[p].pr].numr);
    T[p].num = T[T[p].pl].num + T[T[p].pr].num;
}
void extend(int &p, int las, int num, int l, int r){
    p = ++place;
    if (l == r){
        T[p].num = T[las].num + 2;
        T[p].numl = T[p].numr = 1;
    }
    else {
        int mid = (l + r) / 2;
        if (num <= mid){
            extend(T[p].pl, T[las].pl, num, l, mid);
            T[p].pr = T[las].pr;
        }
        else {
            extend(T[p].pr, T[las].pr, num, mid + 1, r);
            T[p].pl = T[las].pl;
        }
        reget(p);
    }
}
int query(int p, int ql, int qr, int l, int r){
    if (ql == l && qr == r) return T[p].num;
    int mid = (l + r) / 2;
    if (mid >= qr) return query(T[p].pl, ql, qr, l, mid);
        else if (mid < ql) return query(T[p].pr, ql, qr, mid + 1, r);
            else return query(T[p].pl, ql, mid, l, mid) + query(T[p].pr, mid + 1, qr, mid + 1, r);
}
int queryl(int p, int ql, int qr, int l, int r, int ths){
    if (ql == l && qr == r){
        now = max(now, T[p].numl + ths);
        return T[p].num;
    }
    int mid = (l + r) / 2;
    if (mid >= qr) return queryl(T[p].pl, ql, qr, l, mid, ths);
        else if (mid < ql) return queryl(T[p].pr, ql, qr, mid + 1, r, ths);
            else {
                int num = queryl(T[p].pl, ql, mid, l, mid, ths);
                num += queryl(T[p].pr, mid + 1, qr, mid + 1, r, num + ths);
                return num;
            }
}
int queryr(int p, int ql, int qr, int l, int r, int ths){
    if (ql == l && qr == r){
        now = max(now, T[p].numr + ths);
        return T[p].num;
    }
    int mid = (l + r) / 2;
    if (mid >= qr) return queryr(T[p].pl, ql, qr, l, mid, ths);
        else if (mid < ql) return queryr(T[p].pr, ql, qr, mid + 1, r, ths);
            else {
                int num = queryr(T[p].pr, mid + 1, qr, mid + 1, r, ths);
                num += queryr(T[p].pl, ql, mid, l, mid, num + ths);
                return num;
            }
}
bool check(int x){
    int num = query(rt[x], b, c, 1, n); now = -inf; 
    queryr(rt[x], a, b - 1, 1, n, 0);
    num += now; now = - inf;
    queryl(rt[x], c + 1, d, 1, n, 0);
    num += now;
    return num >= 0;
}
bool cmp(Node x, Node y){
    return x.num < y.num;
}
int main(){
//  freopen(".in", "r", stdin);
//  freopen(".out", "w", stdout);
    n = read();
    for (int i = 1; i <= n; i++)
        h[i].num = read(), h[i].id = i;
    sort(h + 1, h + n + 1, cmp);
    L = 1, R = 0;
    for (int i = 1; i <= n; i++){
        if (mp.find(h[i].num) == mp.end())
            mp[h[i].num] = ++R, number[R] = h[i].num;
        in[R].push_back(h[i].id); 
    }
    build(rt[R + 1], 1, n);
    for (int i = R; i >= 1; i--){
        rt[i] = rt[i + 1];
        for (unsigned j = 0; j < in[i].size(); j++)
            extend(rt[i], rt[i], in[i][j], 1, n);
    }
    q = read(); 
    int lastans = 0;
    for (int i = 1; i <= q; i++){
        p[1] = (read() + lastans) % n + 1, p[2] = (read() + lastans) % n + 1;
        p[3] = (read() + lastans) % n + 1, p[4] = (read() + lastans) % n + 1;
        sort(p + 1, p + 4 + 1);
        a = p[1], b = p[2], c = p[3], d = p[4];
        int pl = L, pr = R;
        while (pl <= pr){
            int mid = (pl + pr) / 2;
            if (check(mid))
                pl = mid + 1, lastans = mid;
                else pr = mid - 1;
        }
        lastans = number[lastans];
        printf("%d\n", lastans);
    }
    return 0;
}

你可能感兴趣的:(【可持久化线段树】,【二分】)