值域分块模板

将佬[1]$的值域分块封装了一下,洛谷过了

template <typename T>
struct Block {
    int B, N, tot;
    vector<T> cnt, Cnt, L, R, belong;
    Block(int n): N(n - 1), B(sqrt(n - 1)), cnt(n), Cnt(n), belong(n), L(n), R(n) {
        tot = N / B;
        for (int i = 1; i <= N; ++i)
            belong[i] = (i - 1) / B + 1;
        for (int i = 1; i <= tot; ++i) {
            if (i * B > N) break;
            L[i] = (i - 1) * B + 1;
            R[i] = i * B; //预处理每一块的左右端点
        }
        if (R[tot] < N) {
            tot++, L[tot] = R[tot - 1] + 1, R[tot] = N;
        }
    }

    void add(int x, int k) {
        cnt[x] += k;
        Cnt[belong[x]] += k;
    }
    //求前缀
    T ask(int x) { //get sum in range[1,x]
        T res = 0;
        for (int i = 1; i <= tot; i += 1) {
            if (x <= R[i]) {
                for (int j = L[i]; j <= x; j += 1) {
                    res += cnt[j];
                }
                break;
            } else {
                res += Cnt[i];
            }
        }
        return res;
    }
    //求排名
    T get_rank(int k) { //get rank of k in range[l,r]
        T rank = 1;
        for (int i = 1; i <= tot; ++i) {
            if (k <= R[i]) {//k不如这个块右端点大,那么k一定在这个块中
                for (int j = L[i]; j < k; ++j) { //暴力枚举到k,找到比k小的有几个数
                    rank += cnt[j];
                }
            } else {
                rank += Cnt[i];
            }
        }
        return rank;
    }
    T get_kth(int k) { //get kth number in range[l,r]
        for (int i = 1; i <= tot; ++i) {
            if (k <= Cnt[i]) //如果k出现次数不足这个块中的数的数量了,说明第k名在这个块内
                for (int j = L[i]; j <= R[i]; ++j) {
                    if (cnt[j] == 0) continue;
                    k -= cnt[j];
                    if (k <= 0) {
                        return C[j];
                    }
                }
            else k -= Cnt[i];
        }
        return -1;//未找到,返回-1
    }
    /*
    查k的前驱可以先查k的排名rank,然后查排名为rank-1的数的值
    查k的后继同上
    */
    //查前驱
    T get_pre(const int k) {
        int rank = get_rank(k);
        if (rank == 1) return -2147483647; //k已经是第一名了,不存在前驱
        int pre = get_kth(rank - 1); //查排名前一位元素
        return pre;
    }
    //查后驱
    T get_back(const int k) {
        int rank = get_rank(k);
        if (rank == -1) return 2147483647; //k最大,不存在答案
        int back = cnt[k] > 0 ? get_kth(rank + 1) : get_kth(rank);
        //这里细节一波,因为查的数本身可能出现在序列里,故特判
        if (back == -1) return 2147483647; //没查到后继,不存在答案
        return back;
    }
};

参考文章:


  1. 莫队套值域分块 ↩︎

posted @ 2025-05-23 21:24  Ke_scholar  阅读(9)  评论(0)    收藏  举报