zoukankan      html  css  js  c++  java
  • BZOJ 2653 middle 二分答案+可持久化线段树

      题目大意:有一个序列,包含多次询问。询问区间左右端点在规定区间里移动所得到的最大中位数的值。

      考虑对于每个询问,如何得到最优区间?枚举显然是超时的,只能考虑二分。

      中位数的定义是在一个序列中,比中位数小的数跟比它大的数一样多,由于我们要求的是最大的中位数,自然希望能找到一个区间,使得该区间内比该中位数大的数尽量多。

      二分中位数k,假设比k小的数记为-1,其余的数记为1,那么我们就是要寻找最大连续子序列和,若找到的最大的和>=0,则当前的k就是合法的。

      最大连续子序列和可以用线段树来维护,由于存在多组询问,每次建一棵线段树很慢,我们选择建主席树。初始时,把数按从小到大排好序,全部的数记为1,做到第i个数时,将第i-1个数记为-1,以此做下去。

      那么这样最终二分到的答案会不会不在指定的区间范围之中呢?

      肯定是不存在的,我们可以分类讨论。

       n为偶数:n = 4时,0 1 2 3,中位数的位置为2,如果答案在1-2之间,则会有更优的答案;如果在2-3之间,显然是不可能的。

       n为奇数:n = 3时,0 1 2 3 4,中位数的位置为2,分析同上。

    #include <cstdio>
    #include <cstdlib>
    #include <algorithm>
    
    using namespace std;
    
    typedef long long LL;
    const int maxn = 20005;
    int n, a[maxn], b[maxn], root[maxn], q[10];
    struct Tree
    {
        int cnt;
        int ml[maxn*20], mr[maxn*20], sum[maxn*20], ls[maxn*20], rs[maxn*20];
        Tree()
        {
            cnt = 0;
        }
        void pushup(int rt)
        {
            ml[rt] = max(ml[ls[rt]], sum[ls[rt]]+ml[rs[rt]]);
            mr[rt] = max(mr[rs[rt]], sum[rs[rt]]+mr[ls[rt]]);
            sum[rt] = sum[ls[rt]]+sum[rs[rt]];
        }
        void build(int rt, int l, int r)
        {
            if (l == r)
            {
                ml[rt] = mr[rt] = sum[rt] = 1;
                return ;
            }
            int mid = (l+r)>>1;
            ls[rt] = ++cnt, build(ls[rt], l, mid);
            rs[rt] = ++cnt, build(rs[rt], mid+1, r);
            pushup(rt);
        }
        void update(int las_rt, int rt, int l, int r, int p, int d)
        {
            if (l == r)
            {
                ml[rt] = mr[rt] = sum[rt] = d;
                return ;
            }
            int mid = (l+r)>>1;
            if (p <= mid)
            {
                ls[rt] = ++cnt, rs[rt] = rs[las_rt];
                update(ls[las_rt], ls[rt], l, mid, p, d);
            }
            else
            {
                ls[rt] = ls[las_rt], rs[rt] = ++cnt;
                update(rs[las_rt], rs[rt], mid+1, r, p, d);
            }
            pushup(rt);
        }
        int query_sum(int rt, int l, int r, int L, int R)
        {
            if (L <= l && r <= R)
                return sum[rt];
            int mid = (l+r)>>1, ret = 0;
            if (L <= mid)
                ret += query_sum(ls[rt], l, mid, L, R);
            if (R > mid)
                ret += query_sum(rs[rt], mid+1, r, L, R);
            return ret;
        }
        int query_ml(int rt, int l, int r, int L, int R)
        {
            if (L <= l && r <= R)
                return ml[rt];
            int mid = (l+r)>>1;
            if (R <= mid)
                return query_ml(ls[rt], l, mid, L, R);
            else
                if (L > mid)
                    return query_ml(rs[rt], mid+1, r, L, R);
                else
                    return max(query_ml(ls[rt], l, mid, L, R), query_sum(ls[rt], l, mid, L, R)+query_ml(rs[rt], mid+1, r, L, R));
        }
        int query_mr(int rt, int l, int r, int L, int R)
        {
            if (L <= l && r <= R)
                return mr[rt];
            int mid = (l+r)>>1;
            if (R <= mid)
                return query_mr(ls[rt], l, mid, L, R);
            else
                if (L > mid)
                    return query_mr(rs[rt], mid+1, r, L, R);
                else
                    return max(query_mr(rs[rt], mid+1, r, L, R), query_sum(rs[rt], mid+1, r, L, R)+query_mr(ls[rt], l, mid, L, R));
        }
    }T;
    
    bool cmp(const int &AI, const int &BI)
    {
        return a[AI] < a[BI];
    }
    
    void build_tree()
    {
        root[1] = ++T.cnt;
        T.build(root[1], 1, n);
        for (int i = 2; i <= n; ++i)
        {
            root[i] = ++T.cnt;
            T.update(root[i-1], root[i], 1, n, b[i-1], -1);
        }
    }
    
    bool check(int k, int q1, int q2, int q3, int q4)
    {
        int sum = 0;
        if (q2+1 < q3)
            sum += T.query_sum(root[k], 1, n, q2+1, q3-1);
        sum += T.query_mr(root[k], 1, n, q1, q2);
        sum += T.query_ml(root[k], 1, n, q3, q4);
        return sum >= 0;
    }
    
    int main()
    {
        scanf("%d", &n);
        for (int i = 1; i <= n; ++i)
            scanf("%d", &a[i]), b[i] = i;
        sort(b+1, b+n+1, cmp);
        build_tree();
        int ans = 0;
        int task;
        scanf("%d", &task);
        while (task --)
        {
            scanf("%d %d %d %d", &q[1], &q[2], &q[3], &q[4]);
            for (int i = 1; i <= 4; ++i)
                q[i] = (q[i]+ans)%n+1;
            sort(q+1, q+4+1);
            int l = 1, r = n;
            while (l < r)
            {
                int mid = (l+r+1)>>1;
                if (check(mid, q[1], q[2], q[3], q[4]))
                    l = mid;
                else
                    r = mid-1;
            }
            ans = a[b[l]];
            printf("%d
    ", ans);
        }
        return 0;
    }
    Nothing is impossible!
  • 相关阅读:
    spring data jpa 动态查询(mysql)
    C#面向对象15 多态
    Visual Studio 2012网站如何只生成一个DLL文件
    C#面向对象14 List泛型集合/装箱和拆箱/字典集合(Dictionary)
    C#面向对象13 文件类操作 Path/File/FileStream
    C#面向对象12 集合
    C#面向对象11 里氏转换
    C#面向对象10 继承
    C#面向对象9 字符串
    C# 面向对象8 值类型和引用类型
  • 原文地址:https://www.cnblogs.com/-ZZB-/p/6394175.html
Copyright © 2011-2022 走看看