zoukankan      html  css  js  c++  java
  • luogu2828 [HEOI2016/TJOI2016]排序

    题目大意

      给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序,排序分为两种:(0,l,r)表示将区间[l,r]的数字升序排序;(1,l,r)表示将区间[l,r]的数字降序排序。最后询问第q位置上的数字。n<=30000。

    题解

      关键词:反演。

      我们假设最后q位置上的值为val。此时我们对整个序列进行排序...我们发现除了val外,其它点之间的顺序并不重要,只有其他点与val的相对大小才有意义。所以我们将原序列中位置上的值小于val的的值设为0,大于等于的设为1,整个序列上每个点的值表示的就是序列上的原值与val的大小关系。这样对01值排序用覆盖式的线段树来进行排序过程最方便了(具体看代码中的Sort)。

      此时q位置上的值如果是0,则说明当前的val比答案大;若此时q位置上的值是1,则说明当前的val小于或等于答案。也就是说,val越大,最后q位上的值越有可能是0,val越小,q位上的值越有可能是1。因此我们可以用UpperBound二分得出答案。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cassert>
    using namespace std;
    
    const int MAX_N = 30010, MAX_NODE = MAX_N * 4, MAX_OP = 30010;
    int OrgData[MAX_N];
    int N, TotOp, P;
    
    struct RangeTree
    {
    private:
    
        struct Node
        {
            int Sum, Cover;
        }_nodes[MAX_NODE];
        int N;
    
        void PushDown(int cur, int l, int r)
        {
            if (_nodes[cur].Cover >= 0)
            {
                _nodes[cur * 2].Cover = _nodes[cur].Cover;
                _nodes[cur * 2 + 1].Cover = _nodes[cur].Cover;
                
                int mid = (l + r) / 2;
                _nodes[cur * 2].Sum = _nodes[cur].Cover * (mid - l + 1);
                _nodes[cur * 2 + 1].Sum = _nodes[cur].Cover * (r - mid);
                
                _nodes[cur].Cover = -1;
            }
        }
    
        void PullUp(int cur)
        {
            _nodes[cur].Sum = _nodes[cur * 2].Sum + _nodes[cur * 2 + 1].Sum;
        }
    
        void Update(int cur, int al, int ar, int sl, int sr, int cover)
        {
            assert(al <= sr && ar >= sl && sl <= sr);
            if (al <= sl && sr <= ar)
            {
                _nodes[cur].Cover = cover;
                _nodes[cur].Sum = cover * (sr - sl + 1);
                return;
            }
            PushDown(cur, sl, sr);
            int mid = (sl + sr) / 2;
            if (al <= mid)
                Update(cur * 2, al, ar, sl, mid, cover);
            if (ar > mid)
                Update(cur * 2 + 1, al, ar, mid + 1, sr, cover);
            PullUp(cur);
        }
    
        int Query(int cur, int al, int ar, int sl, int sr)
        {
            assert(al <= sr && ar >= sl && sl <= sr);
            if (al <= sl && sr <= ar)
                return _nodes[cur].Sum;
            PushDown(cur, sl, sr);
            int mid = (sl + sr) / 2, ans = 0;
            if (al <= mid)
                ans += Query(cur * 2, al, ar, sl, mid);
            if (ar > mid)
                ans += Query(cur * 2 + 1, al, ar, mid + 1, sr);
            PullUp(cur);
            return ans;
        }
    
        void InitBuild(int cur, int sl, int sr, int *a)
        {
            if (sl == sr)
            {
                _nodes[cur].Sum = a[sl];
                _nodes[cur].Cover = -1;
                return;
            }
            int mid = (sl + sr) / 2;
            InitBuild(cur * 2, sl, mid, a);
            InitBuild(cur * 2 + 1, mid + 1, sr, a);
            _nodes[cur].Cover = -1;
            PullUp(cur);
        }
    
    public:
    
        void Init(int n, int *a)
        {
            N = n;
            InitBuild(1, 1, N, a);
        }
    
        void Update(int l, int r, int cover)
        {
            if (l > r)
                return;
            Update(1, l, r, 1, N, cover);
        }
    
        int Query(int l, int r)
        {
            return Query(1, l, r, 1, N);
        }
    }g;
    
    struct Oper//operation
    {
        int L, R;
        bool IsUp;
    
        Oper(){}
        Oper(int l, int r, int isUp):L(l),R(r),IsUp(isUp){}
    }_ops[MAX_OP];
    
    void Sort(Oper op)
    {
        int sum1 = g.Query(op.L, op.R);
        if (op.IsUp)
        {
            g.Update(op.R - sum1 + 1, op.R, 1);
            g.Update(op.L, op.R - sum1, 0);
        }
        else
        {
            g.Update(op.L, op.L + sum1 - 1, 1);
            g.Update(op.L + sum1, op.R, 0);
        }
    }
    
    bool AnsNotLesser(int val)
    {
        static int a[MAX_N];
        for (int i = 1; i <= N; i++)
            a[i] = (OrgData[i] >= val);
        g.Init(N, a);
        
        for (int i = 1; i <= TotOp; i++)
            Sort(_ops[i]);
        return g.Query(P, P) == 1;
    }
    
    int UpperBound(int l, int r, bool(*InRange)(int))
    {
        while (l < r)
        {
            int mid = (l + r + 1) / 2;
            if (InRange(mid))
                l = mid;
            else
                r = mid - 1;
        }
        return l;
    }
    
    int main()
    {
        scanf("%d%d", &N, &TotOp);
        for (int i = 1; i <= N; i++)
            scanf("%d", OrgData + i);
        for (int i = 1; i <= TotOp; i++)
        {
            int l, r, isDown;
            scanf("%d%d%d", &isDown, &l, &r);
            _ops[i] = Oper(l, r, !isDown);
        }
        scanf("%d", &P);
        printf("%d
    ", UpperBound(1, N, AnsNotLesser));
        return 0;
    }
    

      

  • 相关阅读:
    CentOS 7.3离线安装 JDK
    七:程序是在何种环境下运行的
    六:亲自尝试压缩数据
    五:内存和磁盘的亲密关系
    四:熟练使用有棱有角的内存
    三:计算机进行小数运算时出错的原因
    二:数据是用二进制数表示的
    一:对程序员来说CPU是什么?
    单元测试的艺术-入门篇
    蔡康永的说话之道2-透过说话,懂得把别放在心上
  • 原文地址:https://www.cnblogs.com/headboy2002/p/9217075.html
Copyright © 2011-2022 走看看