洛谷P3380 二逼平衡树

58 篇文章 0 订阅

Address


Solution

  • 好像没什么人写树状数组套主席树,其实常数挺小的说……
  • 前三个都是基础操作了,而对于前驱后继,我们可以拆成两个操作,先算它在区间中排名,再查找对应的值。
  • 空间上因为重复节点很多,稍微卡卡也能过了。
  • 时间复杂度 O(nlog2n) O ( n log 2 ⁡ n )

Code(洛谷)

#include <iostream>
#include <cstdio>
#include <cctype>
#include <algorithm>
#include <cstring>

using namespace std;

namespace inout
{
    const int S = 1 << 20;
    char frd[S], *ihed = frd + S;
    const char *ital = ihed;

    inline char inChar()
    {
        if (ihed == ital)
            fread(frd, 1, S, stdin), ihed = frd;
        return *ihed++;
    }

    inline int get()
    {
        char ch; int res = 0; bool flag = false;
        while (!isdigit(ch = inChar()) && ch != '-');
        (ch == '-' ? flag = true : res = ch ^ 48);
        while (isdigit(ch = inChar()))
            res = res * 10 + ch - 48;
        return flag ? -res : res;
    }

    char fwt[S], *ohed = fwt;
    const char *otal = ohed + S;

    inline void outChar(char ch)
    {
        if (ohed == otal)   
            fwrite(fwt, 1, S, stdout), ohed = fwt;
        *ohed++ = ch;
    }

    inline void put(int x)
    {
        if (x > 9) put(x / 10);
        outChar(x % 10 + 48);
    }   
};
using namespace inout;

const int L = 1e8;
const int M = 2e7 + 5, N = 5e4 + 5;
int rt[N], a[N], lx[N], rx[N], low[N];
int ql, qr, n, m, T, tmp; bool flag;

struct Chairman
{
    int lc, rc, cnt;
    #define l(x) tr[x].lc
    #define r(x) tr[x].rc
    #define c(x) tr[x].cnt
}tr[M];

inline void Init(int l, int r)
{
    ql = qr = 0;
    for (int i = l - 1; i; i -= low[i]) 
        lx[++ql] = rt[i];
    for (int i = r; i; i -= low[i])
        rx[++qr] = rt[i];
}

inline void ChangeL()
{
    for (int i = 1; i <= ql; ++i) 
        lx[i] = l(lx[i]);
    for (int i = 1; i <= qr; ++i) 
        rx[i] = l(rx[i]); 
}

inline void ChangeR()
{
    for (int i = 1; i <= ql; ++i) 
        lx[i] = r(lx[i]);
    for (int i = 1; i <= qr; ++i) 
        rx[i] = r(rx[i]);
}

inline void Insert(int &x, int l, int r, int v)
{
    if (!x) 
        x = ++T, l(x) = r(x) = c(x) = 0; 
    ++c(x); 
    if (l == r) return ;
    int mid = l + r >> 1;
    if (v <= mid) Insert(l(x), l, mid, v);
        else Insert(r(x), mid + 1, r, v); 
}

inline void Modify(int x, int l, int r, int v)
{
    --c(x);
    if (l == r) return ;
    int mid = l + r >> 1;
    if (v <= mid) Modify(l(x), l, mid, v);
        else Modify(r(x), mid + 1, r, v);
}

inline int queryKth(int l, int r, int v)
{
    if (l == r) 
    {
        int res = 0;
        if (flag)
        {
            for (int i = 1; i <= ql; ++i) 
                res -= c(lx[i]);
            for (int i = 1; i <= qr; ++i) 
                res += c(rx[i]);
        }
        return res;
    }

    int mid = l + r >> 1;
    if (v <= mid)
        return ChangeL(), queryKth(l, mid, v);
    else 
    {
        int res = 0;
        for (int i = 1; i <= ql; ++i) 
            res -= c(l(lx[i]));
        for (int i = 1; i <= qr; ++i) 
            res += c(l(rx[i]));
        return ChangeR(), res + queryKth(mid + 1, r, v);
    }
}

inline int findKth(int l, int r, int k)
{
    if (l == r) return l;

    int res = 0;
    for (int i = 1; i <= ql; ++i) 
        res -= c(l(lx[i]));
    for (int i = 1; i <= qr; ++i) 
        res += c(l(rx[i]));

    int mid = l + r >> 1;
    if (k <= res) 
        return ChangeL(), findKth(l, mid, k);
    else
        return ChangeR(), findKth(mid + 1, r, k - res);
}

int main()
{
    n = get(); m = get();
    for (int i = 1; i <= n; ++i) low[i] = i & -i;
    for (int i = 1; i <= n; ++i)
    {
        a[i] = get();
        for (int j = i; j <= n; j += low[j])
            Insert(rt[j], 0, L, a[i]);
    }

    int opt, l, r, k;
    while (m--)
    {
        opt = get(); l = get(); r = get();
        switch (opt)
        {
            case 1:
                flag = false; Init(l, r);
                put(queryKth(0, L, get()) + 1), outChar('\n');
            break;

            case 2:
                Init(l, r);
                put(findKth(0, L, get())), outChar('\n');
            break;

            case 3:
                for (int i = l; i <= n; i += low[i])
                    Modify(rt[i], 0, L, a[l]);
                a[l] = r;
                for (int i = l; i <= n; i += low[i])
                    Insert(rt[i], 0, L, a[l]);
            break;

            case 4:
                flag = false; Init(l, r);
                tmp = queryKth(0, L, k = get());
                if (!tmp) 
                    outChar('-'), put(2147483647), outChar('\n');
                else 
                    Init(l, r), put(findKth(0, L, tmp)), outChar('\n'); 
            break;

            case 5:
                flag = true; Init(l, r);
                tmp = queryKth(0, L, k = get());
                if (tmp == r - l + 1) 
                    put(2147483647), outChar('\n');
                else 
                    Init(l, r), put(findKth(0, L, tmp + 1)), outChar('\n');
            break;
        }   
    }
    fwrite(fwt, 1, ohed - fwt, stdout);
    return 0; 
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值