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\n", UpperBound(1, N, AnsNotLesser));
    return 0;
}

  

posted @ 2018-06-23 13:54  headboy2002  阅读(95)  评论(0编辑  收藏  举报