Skip to content

Sqrt Tree ​

预处理O(nlog⁡log⁡n)O(n \log \log n),单点修改O(n)O(\sqrt n),查询O(1)O(1)

cpp
// 区间查询的聚合信息结构体(根据需求自定\left|\bigcap_{i=1}^{n}\{x_i \leq b_i\}\right| = 
\sum_{k=0}^{n}(-1)^{k} 
\sum_{1\leq i_1 < \cdots < i_k \leq n} 
\binom{ \left( m - \sum_{j=1}^{k}(b_{i_j} + 1) \right) + n - 1 }{ n - 1 })
struct Info {
    // 示例:可包含最小值、和等聚合信息
    // 需要根据实际需求实现合并操作
};

// 平方分解树模板
// T: 元素类型,需要支持默认构造函数
// F: 合并函数类型,需实现 operator()(const T&, const T&)
template <typename T>
class SqrtTree {
private:
    // 基础数据参数
    int n;          // 原始数据元素个数
    int lg;         // log2(n) 的上界值
    int indexSz;    // 索引块的数量
    
    vector<T> v;    // 数据存储(包含辅助索引块)
    
    // 分层管理结构
    vector<int> clz;       // 前导零数量表(用于快速计算最高有效位)
    vector<int> layers;    // 每层的块大小层级(以log2表示)
    vector<int> onLayer;   // 每个可能的log值对应的层级
    
    // 预处理数据结构
    vector<vector<T>> pref;   // 各层的前缀聚合值
    vector<vector<T>> suf;    // 各层的后缀聚合值
    vector<vector<T>> between;// 层间块聚合值
    

    // 计算不小于n的最小2的幂次
    static int log2_ceil(int n) {
        int res = 0;
        while ((1 << res) < n) res++;
        return res;
    }
    static T f(const T& a, const T& b) {
        // 合并函数:根据需求实现
        return a+b;
    }
    // 构建单个块的前后缀聚合
    // layer: 当前处理层
    // l: 块左边界(包含)
    // r: 块右边界(不包含)
    void buildBlock(int layer, int l, int r) {
        // 计算前缀聚合:pref[i] = v[l] ⊗ v[l+1] ⊗ ... ⊗ v[i]
        pref[layer][l] = v[l];
        for (int i = l+1; i < r; i++)
            pref[layer][i] = f(pref[layer][i-1], v[i]);
        
        // 计算后缀聚合:suf[i] = v[i] ⊗ v[i+1] ⊗ ... ⊗ v[r-1]
        suf[layer][r-1] = v[r-1];
        for (int i = r-2; i >= l; i--)
            suf[layer][i] = f(v[i], suf[layer][i+1]);
    }

    // 构建层间块聚合结构
    // layer: 当前层(必须>=1)
    // lBound: 当前处理区间的左边界
    // rBound: 当前处理区间的右边界
    // betweenOffs: 当前层在between数组中的偏移量
    void buildBetween(int layer, int lBound, int rBound, int betweenOffs) {
        int bSzLog = (layers[layer]+1) >> 1; // 块大小的log值
        int bCntLog = layers[layer] >> 1;    // 块数量的log值
        int bSz = 1 << bSzLog;               // 块的实际大小
        int bCnt = (rBound - lBound + bSz - 1) >> bSzLog; // 总块数
        
        // 预计算所有块区间组合的聚合值
        for (int i = 0; i < bCnt; i++) {
            T ans;
            for (int j = i; j < bCnt; j++) {
                // 获取第j块的聚合值(来自后缀数组)
                T add = suf[layer][lBound + (j << bSzLog)];
                // 合并区间[i,j]的聚合结果
                ans = (i == j) ? add : f(ans, add);
                // 存储到between数组
                between[layer-1][betweenOffs + lBound + (i << bCntLog) + j] = ans;
            }
        }
    }

    // 构建第0层的特殊索引结构
    void buildBetweenZero() {
        int bSzLog = (lg + 1) >> 1;  // 基础块大小
        // 将每个块的聚合值存入辅助空间
        for (int i = 0; i < indexSz; i++)
            v[n + i] = suf[0][i << bSzLog];
        // 递归构建上层结构
        build(1, n, n + indexSz, (1 << lg) - n);
    }

    // 更新第0层的索引块
    void updateBetweenZero(int bid) {
        int bSzLog = (lg + 1) >> 1;
        v[n + bid] = suf[0][bid << bSzLog];
        update(1, n, n + indexSz, (1 << lg) - n, n + bid);
    }

    // 递归构建树结构
    // layer: 当前处理层
    // lBound: 当前处理的左边界
    // rBound: 当前处理的右边界
    // betweenOffs: 层间结构的存储偏移
    void build(int layer, int lBound, int rBound, int betweenOffs) {
        if (layer >= (int)layers.size()) return;
        
        int bSz = 1 << ((layers[layer] + 1) >> 1); // 当前层块大小
        // 遍历处理每个块
        for (int l = lBound; l < rBound; l += bSz) {
            int r = min(l + bSz, rBound);
            buildBlock(layer, l, r);      // 构建当前块的前后缀
            build(layer + 1, l, r, betweenOffs); // 递归构建下层
        }
        
        // 特殊处理第0层
        if (layer == 0) buildBetweenZero();
        else buildBetween(layer, lBound, rBound, betweenOffs);
    }

    // 更新操作的核心实现
    void update(int layer, int lBound, int rBound, int betweenOffs, int x) {
        if (layer >= (int)layers.size()) return;
        
        int bSzLog = (layers[layer] + 1) >> 1;  // 当前层块大小的log值
        int bSz = 1 << bSzLog;                  // 块的实际大小
        int blockIdx = (x - lBound) >> bSzLog;   // 定位块索引
        int l = lBound + (blockIdx << bSzLog);   // 块左边界
        int r = min(l + bSz, rBound);            // 块右边界
        
        // 重建所在块的前后缀
        buildBlock(layer, l, r);
        // 更新上层索引
        if (layer == 0) updateBetweenZero(blockIdx);
        else buildBetween(layer, lBound, rBound, betweenOffs);
        
        // 递归更新上层结构
        update(layer + 1, l, r, betweenOffs, x);
    }

    // 区间查询的核心实现
    T query(int l, int r, int betweenOffs, int base) {
        if (l == r) return v[l];          // 单点查询
        if (l + 1 == r) return f(v[l], v[r]); // 相邻点直接合并
        
        // 计算异或值用于确定层级
        int xorVal = (l - base) ^ (r - base);
        int layer = onLayer[clz[xorVal]]; // 选择最高有效位对应的层
        
        int bSzLog = (layers[layer] + 1) >> 1; // 当前层块大小的log值
        int bCntLog = layers[layer] >> 1;      // 块数量的log值
        
        // 计算当前层的左边界
        int lBound = ((((l - base) >> layers[layer]) << layers[layer]) + base);
        
        // 确定左右块索引
        int lBlock = ((l - lBound) >> bSzLog) + 1;
        int rBlock = ((r - lBound) >> bSzLog) - 1;
        
        T ans = suf[layer][l]; // 初始化为左端后缀
        
        // 处理中间完整块
        if (lBlock <= rBlock) {
            T add;
            if (layer == 0) {
                // 第0层需要递归查询索引块
                add = query(n + lBlock, n + rBlock, (1 << lg) - n, n);
            } else {
                // 上层直接从between数组获取
                add = between[layer-1][betweenOffs + lBound 
                    + (lBlock << bCntLog) + rBlock];
            }
            ans = f(ans, add);
        }
        
        // 合并右端前缀
        ans = f(ans, pref[layer][r]);
        return ans;
    }

public:
    // 构造函数
    // a: 原始数据数组
    // f: 合并函数(默认构造)
    SqrtTree(const vector<T>& a) : 
        n(a.size()), 
        lg(log2_ceil(n)),
        v(a),
        clz(1 << lg),     // 前导零表大小为2^lg
        onLayer(lg + 1)  // 层级映射表
    {
        // 初始化前导零表
        clz[0] = 0;
        for (int i = 1; i < (1 << lg); i++)
            clz[i] = clz[i >> 1] + 1;
        
        // 构建层级结构
        int tlg = lg;
        while (tlg > 1) {
            onLayer[tlg] = layers.size();
            layers.push_back(tlg);
            tlg = (tlg + 1) >> 1;  // 按log递减方式构建层
        }
        // 填充剩余的层级映射
        for (int i = lg-1; i >= 0; i--)
            onLayer[i] = max(onLayer[i], onLayer[i+1]);
        
        // 计算索引块参数
        int betweenLayers = max((int)0, (int)layers.size()-1);
        int bSzLog = (lg + 1) >> 1;
        int bSz = 1 << bSzLog;
        indexSz = (n + bSz - 1) >> bSzLog; // 向上取整
        
        // 调整数据存储大小
        v.resize(n + indexSz);
        
        // 初始化预处理数组
        pref.assign(layers.size(), vector<T>(n + indexSz));
        suf.assign(layers.size(), vector<T>(n + indexSz));
        between.assign(betweenLayers, vector<T>((1 << lg) + bSz));
        
        // 从第0层开始构建
        build(0, 0, n, 0);
    }

    // 区间查询接口 [l, r]
    T query(int l, int r) { 
        return query(l, r, 0, 0); 
    }

    // 单点更新接口
    void update(int x, const T& item) {
        v[x] = item;       // 更新原始数据
        update(0, 0, n, 0, x); // 从底层开始更新结构
    }
};