「BZOJ2653」middle-二分+主席树

Decription

回答 Q Q 个这样的询问:

序列 s s 的左端点在 [a,b] [ a , b ] 之间,右端点在 [c,d] [ c , d ] 之间的子序列中,最大的中位数。其中 a<b<c<d a < b < c < d ,位置也从0开始标号。

Solution

对于每个询问二分答案 k k ,比 k k 小的设为 1 − 1 ,大于等于 k k 则设为 1 1 ,判断能否使区间和 0 ≥ 0 .

考虑按照大小顺序构建二分时的 (1,1) ( 1 , − 1 ) 序列,那么每次只会修改一个位置,主席树维护即可。

#include 
using namespace std;

const int maxn = 20005;

int n, A[maxn], Id[maxn], Q;

inline int gi()
{
    char c = getchar();
    while (c < '0' || c > '9') c = getchar();
    int sum = 0;
    while ('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
    return sum;
}

int root[maxn];
namespace pret
{
    struct node {
        int sum, suml, sumr;
        int lch, rch;

        void clear()
        {
            sum = 0; suml = -n - 1; sumr = -n - 1;
        }
    } t[maxn * 20], Ans;
    int tot;

    #define mid ((l + r) >> 1)

    void merge(node &s, node a, node b)
    {
        s.sum = a.sum + b.sum;
        s.suml = max(a.suml, b.suml + a.sum);
        s.sumr = max(b.sumr, a.sumr + b.sum);
    }

    void build(int &s, int l, int r)
    {
        s = ++tot;
        if (l == r) {
            t[s].sum = t[s].suml = t[s].sumr = 1;
            return ;    
        }
        build(t[s].lch, l, mid);
        build(t[s].rch, mid + 1, r);
        merge(t[s], t[t[s].lch], t[t[s].rch]);
    }

    void modify(int &s, int l, int r, int v)
    {
        t[++tot] = t[s]; s = tot;
        if (l == r) {
            t[s].sum = t[s].suml = t[s].sumr = -1;
            return ;
        }
        if (v <= mid) modify(t[s].lch, l, mid, v);
        else modify(t[s].rch, mid + 1, r, v);
        merge(t[s], t[t[s].lch], t[t[s].rch]);
    }

    void query(int s, int l, int r, int x, int y)
    {
        if (x <= l && r <= y) {
            merge(Ans, Ans, t[s]); return ;
        }
        if (x <= mid) query(t[s].lch, l, mid, x, y);
        if (y >= mid + 1) query(t[s].rch, mid + 1, r, x, y);
    }

}

inline bool cmp(const int &x, const int &y)
{
    return A[x] < A[y];
}

int a, b, c, d;
inline bool check(int x)
{
    int sum = 0;
    pret::Ans.clear();
    if (b + 1 <= c - 1) pret::query(root[x], 1, n, b + 1, c - 1), sum += pret::Ans.sum;
    pret::Ans.clear(); pret::query(root[x], 1, n, a, b); sum += pret::Ans.sumr;
    pret::Ans.clear(); pret::query(root[x], 1, n, c, d); sum += pret::Ans.suml;
    return sum >= 0;
}

int main()
{
    freopen("middle.in", "r", stdin);
    freopen("middle.out", "w", stdout);

    n = gi();
    for (int i = 1; i <= n; ++i) A[i] = gi(), Id[i] = i;

    sort(Id + 1, Id + n + 1, cmp);
    pret::build(root[1], 1, n);
    for (int i = 2; i <= n; ++i)
        root[i] = root[i - 1], pret::modify(root[i], 1, n, Id[i - 1]);

    int lastans = 0;
    Q = gi();
    for (int i = 1; i <= Q; ++i) {
        a = gi(); b = gi(); c = gi(); d = gi();
        int q[4] = {(a + lastans) % n, (b + lastans) % n, (c + lastans) % n, (d + lastans) % n};
        sort(q, q + 4);
        a = q[0] + 1; b = q[1] + 1; c = q[2] + 1; d = q[3] + 1;
        int l = 1, r = n, Mid;
        while (l < r) {
            Mid = (l + r + 1) >> 1;
            if (check(Mid)) l = Mid;
            else r = Mid - 1;
        }
        printf("%d\n", lastans = A[Id[l]]);
    }

    return 0;
}

你可能感兴趣的:(文章类型——题解,数据结构——主席树,算法——二分)