【国家集训队】middle

题意:给你一个长度为n的序列s。
回答Q个这样的询问:s的左端点在[a,b]之间,右端点在[c,d]之间的子序列中,最大的中位数。
强制在线。
思路:首先考虑二分答案,判断可行的方法则是看是否小于他的数可以比大于他的数多。则考虑对于[b+1, c-1]直接求出来,对于[a, b]求最大的小的减大的后缀,[c, d]求最大前缀,可以建线段树维护。对于每一个数建一个线段树,则空间爆了,考虑使用主席树维护
那么对于第i颗主席树,则是小于 i 的都是-1,大于等于 i 的是1,查询的时候二分答案查询在排名 i 的这颗树上查是否能选出序列使得和大于等于0
https://www.luogu.org/problemnew/show/P2839

#include 
#include 
#include 
#include 
#include 
#define MAXN 20050
using namespace std;
inline int read(){
    int f = 1, ret = 0; char ch = getchar();
    while(ch < '0' || ch > '9') if(ch == '-') f = -1, ch = getchar();
    while(ch >= '0' && ch <= '9') ret = ret*10+int(ch-'0'), ch = getchar();
    return ret*f;
}
void print(int x){
    if(x < 10) putchar(x+'0');
    else{
        print(x/10);
        putchar(x%10+'0');
    }
}
int n, cnt, Q, lastans, q[4], root[MAXN];
struct node1{
    int id, num;
    bool operator < (const node1 &x) const{
        return num < x.num;
    }
    node1(){}
    node1(int _id, int _num){
        id = _id, num = _num;
    }
}a[MAXN<<2];
struct data{
    int lmax, rmax, tot;
};
struct node{
    int ls, rs;
    data d;
}t[MAXN<<6];
data merge(data a, data b){
    data ret;
    ret.tot = a.tot + b.tot;
    ret.lmax = max(a.lmax, a.tot + b.lmax);
    ret.rmax = max(b.rmax, b.tot + a.rmax);
    return ret;
}
void build(int &u, int l, int r){
    int tmp = u; u = ++cnt; t[u] = t[tmp];
    if(l == r) {t[u].d.tot = t[u].d.lmax = t[u].d.rmax = 1; return;}
    int mid = (l+r)>>1;
    build(t[u].ls, l, mid);
    build(t[u].rs, mid+1, r);
    t[u].d = merge(t[t[u].ls].d, t[t[u].rs].d);
}
void update(int &u, int l, int r, int x){
    int tmp = u; u = ++cnt; t[u] = t[tmp];
    if(l == r){t[u].d.tot = t[u].d.lmax = t[u].d.rmax = -1; return;}
    int mid = (l+r)>>1;
    if(x <= mid) update(t[u].ls, l, mid, x);
    else update(t[u].rs, mid+1, r, x);
    t[u].d = merge(t[t[u].ls].d, t[t[u].rs].d);
}
data query(int u, int l, int r, int tl, int tr){
    if(tl <= l && r <= tr) return t[u].d;
    int mid = (l+r)>>1;
    if(tr <= mid) return query(t[u].ls, l, mid, tl, tr);
    if(mid < tl) return query(t[u].rs, mid+1, r, tl, tr);
    else return merge(query(t[u].ls, l, mid, tl, tr), query(t[u].rs, mid+1, r, tl, tr));
}
bool check(int id, int l1, int r1, int l2, int r2){
    int ret = 0;
    if(r1+1 <= l2-1) ret += query(root[id], 1, n, r1+1, l2-1).tot;
    ret += query(root[id], 1, n, l1, r1).rmax;
    ret += query(root[id], 1, n, l2, r2).lmax;
    //printf("%d %d\n", id, ret);
    return ret>=0;
}
/*
输入格式:
第一行序列长度n。
接下来n行按顺序给出a中的数。
接下来一行Q
然后Q行每行a,b,c,d,我们令上个询问的答案是x(如果这是第一个询问则x=0)。
令数组q={(a+x)%n,(b+x)%n,(c+x)%n,(d+x)%n}。

将q从小到大排序之后,令真正的要询问的a=q[0],b=q[1],c=q[2],d=q[3]。
n<=20000,Q<=25000*/
int main(){
    //freopen("1.in", "r", stdin);
    //freopen("1.out", "w", stdout);
    scanf("%d", &n);
    //n = read();
    for(int i = 1; i <= n; i++){
        scanf("%d", &a[i].num); a[i].id = i;
        //a[i] = node1(i, read());
    }
    sort(a+1, a+n+1);
    build(root[1], 1, n);
    for(int i = 2; i <= n; i++) root[i] = root[i-1], update(root[i], 1, n, a[i-1].id);
    scanf("%d", &Q);//Q = read();
    while(Q--){
        for(int i = 0; i < 4; i++) scanf("%d", &q[i]), q[i] = (q[i]+lastans)%n;//q[i] = (read()+lastans)%n;
        sort(q, q+4); for(int i = 0; i < 4; i++) q[i]++;
        int l = 1, r = n, mid, ans;
        while(l <= r){
            mid = (l+r)>>1;
            if(check(mid, q[0], q[1], q[2], q[3])) ans = mid, l = mid+1;
            else r = mid-1;
        }
        printf("%d\n", lastans = a[ans].num);
        //print(lastans = a[ans].num); putchar('\n'); 
    }
    return 0;
}

你可能感兴趣的:(题解)