P2839 [国家集训队]middle

一个长度为 \(n\) 的序列 \(a\),设其排过序之后为 \(b\),其中位数定义为 \(b_{n/2}\),其中 \(a,b\)\(0\) 开始标号,除法取下整。

给你一个长度为 \(n\) 的序列 \(s\)

回答 \(Q\) 个这样的询问:\(s\) 的左端点在 \([a,b]\) 之间,右端点在 \([c,d]\) 之间的子区间中,最大的中位数。

其中 \(a<b<c<d\)

位置也从 \(0\) 开始标号。

我会使用一些方式强制你在线。

比较清新的难题,这次我终于没有抄代码自己写出来了……
这里要求最大中位数,可以考虑二分答案。将大于等于mid的数设为1,小于mid的数设为0。
如果一个区间的和大于等于0,那么显然答案大于等于mid。
所以询问其实就是要找左端点在[a,b],右端点在[c,d]的最大的区间和,[b+1, c-1]的和是固定的,问题转化为对[a,b], [c,d]区间分别求右侧的最大子段和和左侧的最大子段和。
这个可以用线段树维护。离散化之后,我们的mid只有n个不同的取值。
对于mid 和 mid + 1,其实只有等于mid的那个数的位置发生了变化。然后惊讶发现每次只改变一个位置的值不是主席树吗。
主席树比较容易错的地方就是建树的时候应该按照什么样的顺序,这里要按照数的大小顺序。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fi first
#define se second
using namespace std;

const int N = 1e5 + 100;
typedef long long ll;
typedef pair<ll, ll> pll;
namespace Segment{
    int rt[N<<5], ls[N << 5], rs[N << 5];
    ll lmx[N << 5], rmx[N << 5], sum[N << 5], mx[N << 5];
    int tot;

    void push_up(int x) {
        sum[x] = sum[ls[x]] + sum[rs[x]];
        lmx[x] = max(lmx[ls[x]], sum[ls[x]] + lmx[rs[x]]);
        rmx[x] = max(rmx[rs[x]], sum[rs[x]] + rmx[ls[x]]);
        mx[x] = max(lmx[x], rmx[x]);
    }

    void build(int &x,int l, int r) {
        if (!x) x = ++tot;
        if (l == r) {
            lmx[x] = rmx[x] = mx[x] = 1;
            sum[x] = 1;
            return ;
        }
        int mid = (l + r) >> 1;
        build(ls[x], l, mid);
        build(rs[x], mid + 1, r);
        push_up(x);
    }

    void insert(int y,int &x, int l, int r, int pos) {
        if (!x) x = ++tot;
        if (l == r) {
            lmx[x] = rmx[x] = mx[x] =  0;
            sum[x] = -1;
            return ;
        }
        int mid = (l + r) >> 1;
        if (pos <= mid) insert(ls[y],ls[x], l, mid, pos), rs[x] = rs[y];
        else insert(rs[y], rs[x], mid + 1, r, pos), ls[x] = ls[y];
        push_up(x);
    }

    pll query_lmx(int x, int l, int r, int L, int R) {
        if (l > R || r < L) return make_pair(0,0);
        if (L <= l && r <= R) {
            return make_pair(lmx[x], sum[x]);
        }
        int mid = (l + r) >> 1;
        pll i = query_lmx(ls[x], l, mid, L, R);
        pll j = query_lmx(rs[x], mid + 1, r, L, R);
        return make_pair(max(i.first, i.second + j.first), i.second + j.second);
    }

    pll query_rmx(int x, int l, int r, int L, int R) {
        if (l > R || r < L) return make_pair(0, 0);
        if (L <= l && r <= R) {
            return make_pair(rmx[x], sum[x]);
        }
        int mid = (l + r) >> 1;
        pll i = query_rmx(ls[x], l, mid, L, R);
        pll j = query_rmx(rs[x], mid + 1, r, L, R);
        return make_pair(max(j.first, i.first + j.second), i.second + j.second);
    }
   
    long long query_sum(int x, int l, int r, int L ,int R) {
        if (l > R || r < L ) return 0;
        if (L <= l && r <= R) {
            return sum[x];
        }
        int mid = (l + r) >> 1;
        return query_sum(ls[x], l, mid, L, R) + query_sum(rs[x], mid + 1, r , L, R);
    }
}
using namespace Segment;
int a[N], b[N], n, m, lastans;
int p[N];
int tmp[5];


bool cmp(int x, int y) {
    return a[x] < a[y];
}
int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]), b[i] = a[i], p[i] = i;
    sort(b + 1, b + n + 1);
    sort(p + 1, p + n + 1, cmp);
    for (int i = 1; i <= n; i++) a[i] = lower_bound(b + 1, b + n + 1, a[i]) - b;
    //for (int i = 1; i <= n; i++) cout << a[i] << " " ; cout << endl;
    Segment::build(rt[1], 1, n);
    for (int i = 2; i <= n; i++) {
        insert(rt[i-1], rt[i], 1, n, p[i-1]);
    }
    scanf("%d", &m);
    for (int i = 1; i <= m; i++) {
        int A, B, C, D;
        scanf("%d%d%d%d", &A, &B, &C, &D);
        A = (A + lastans) % n;tmp[1] = A+1;
        B = (B + lastans) % n;tmp[2] = B+1;
        C = (C + lastans) % n;tmp[3] = C+1;
        D = (D + lastans) % n;tmp[4] = D+1;
        sort(tmp + 1, tmp + 5);
        A = tmp[1], B = tmp[2], C = tmp[3], D = tmp[4];
        int l = 1, r = n;
        while (l < r) {
            int mid = (l + r + 1) >> 1;
            //cout << "mid !! " << mid << endl;
            ll s = query_lmx(rt[mid], 1, n, C+1, D).first + query_rmx(rt[mid], 1, n, A, B-1).first + query_sum(rt[mid], 1, n, B, C);
            //cout << "s " << s << endl;
            if (s >= 0) l = mid; 
            else r = mid - 1;
        }
        printf("%d\n", b[l]);
        lastans = b[l];
    }
}
posted @ 2020-08-01 10:55  iwwi  阅读(139)  评论(0)    收藏  举报