[BZOJ3110][ZJOI2013]K大数查询(线段树套线段树)

有很多种解法。这里讲一种线段树套线段树的解法。这里外层的线段树是权值线段树,而内层的是位置线段树(保存对应权值范围的出现次数)。在空间问题上,使用动态开点。
对于修改,可以看作是对于权值 c ,位置区间[a,b]内的所有出现次数加 1 。也就是说,对于权值线段树从根节点到权值c代表的叶子节点的路径上的所有点所嵌套的位置线段树,都进行一次区间修改,即区间 [a,b] 1 ,可以打标记。
对于询问,可以在权值线段树上二分,即如果右子节点(右子权值区间)所嵌套的线段树的[a,b]区间和小于等于 c ,就往右子树走,否则往左子树走,并且把c减去刚刚的询问结果。走到叶子节点时,答案就是该叶子节点对应的权值。
整体二分解法:http://blog.csdn.net/qq_18455665/article/details/50707462
代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define p2 p << 1
#define p3 p << 1 | 1
using namespace std;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
typedef unsigned int ui;
typedef long long ll;
const int N = 4e5 + 5, M = 18e6 + 5;
int n, m, rt[N], QAQ;
struct cyx {
    int lc, rc; ui sum, add;
    void init() {
        lc = rc = sum = add = 0;
    }
} T[M];
inline void modify(const int &l, const int &r, const int &s,
const int &e, int &p) {
    if (!p) T[p = ++QAQ].init();
    if (l == s && r == e) return (void) (T[p].add++);
    int mid = l + r >> 1; if (T[p].add) {
        if (!T[p].lc) T[T[p].lc = ++QAQ].init();
        if (!T[p].rc) T[T[p].rc = ++QAQ].init();
        T[T[p].lc].add += T[p].add; T[T[p].rc].add += T[p].add; T[p].add = 0; 
    }
    if (e <= mid) modify(l, mid, s, e, T[p].lc);
    else if (s >= mid + 1) modify(mid + 1, r, s, e, T[p].rc);
    else modify(l, mid, s, mid, T[p].lc),
        modify(mid + 1, r, mid + 1, e, T[p].rc);
    T[p].sum = T[T[p].lc].sum + T[T[p].rc].sum +
        T[T[p].lc].add * (mid - l + 1) + T[T[p].rc].add * (r - mid);
}
inline void change(const int &l, const int &r, const int &s,
const int &e, const int &v, const int &p) {
    modify(1, n, s, e, rt[p]); if (l == r) return; int mid = l + r >> 1;
    if (v <= mid) change(l, mid, s, e, v, p2);
    else change(mid + 1, r, s, e, v, p3);
}
inline ui query(const int &l, const int &r, const int &s,
const int &e, int &p) {
    if (!p) T[p = ++QAQ].init();
    if (l == s && r == e) return T[p].sum + T[p].add * (r - l + 1);
    int mid = l + r >> 1; if (T[p].add) {
        if (!T[p].lc) T[T[p].lc = ++QAQ].init();
        if (!T[p].rc) T[T[p].rc = ++QAQ].init();
        T[T[p].lc].add += T[p].add; T[T[p].rc].add += T[p].add; T[p].add = 0; 
    }
    ui res; if (e <= mid) res = query(l, mid, s, e, T[p].lc);
    else if (s >= mid + 1) res = query(mid + 1, r, s, e, T[p].rc);
    else res = query(l, mid, s, mid, T[p].lc) +
        query(mid + 1, r, mid + 1, e, T[p].rc);
    T[p].sum = T[T[p].lc].sum + T[T[p].rc].sum +
        T[T[p].lc].add * (mid - l + 1) + T[T[p].rc].add * (r - mid);
    return res;
}
inline int ask(const int &l, const int &r, const int &s,
const int &e, const ll &c, const int &p) {
    if (l == r) return l; int mid = l + r >> 1;
    ui tmp = query(1, n, s, e, rt[p3]);
    if (c <= tmp) return ask(mid + 1, r, s, e, c, p3);
    else return ask(l, mid, s, e, c - tmp, p2);
}
int main() {
    int a, b, c, op; n = read(); m = read(); ll d;
    while (m--) {
        op = read(); a = read(); b = read();
        op == 1 ? c = read() : scanf("%lld", &d);
        if (op == 1) change(1, n, a, b, c, 1);
        else printf("%d\n", ask(1, n, a, b, d, 1));
    }
    return 0;
}
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值