差分数组与倍增算法

差分数组和倍增算法是处理区间操作和树查询的强大技术。本节涵盖一维/多维差分数组、稀疏表(ST)用于区间最值查询,以及二进制提升用于最近公共祖先(LCA)问题。

差分数组

差分数组通过存储连续元素之间的差值,实现O(1)时间内的区间更新。

一维和二维差分数组

#include <iostream>
#include <vector>
using namespace std;

class DifferenceArray1D {
private:
    vector<long long> diff;
    int n;
    
public:
    DifferenceArray1D(vector<int>& arr) : n(arr.size()) {
        diff.resize(n + 1, 0);
        for (int i = 0; i < n; i++) {
            diff[i] += arr[i];
            diff[i + 1] -= arr[i];
        }
    }
    
    // 在区间[l, r]上加上value
    void rangeAdd(int l, int r, int value) {
        diff[l] += value;
        if (r + 1 <= n) {
            diff[r + 1] -= value;
        }
    }
    
    // 获取所有更新后的最终数组
    vector<long long> getArray() {
        vector<long long> result(n);
        result[0] = diff[0];
        for (int i = 1; i < n; i++) {
            result[i] = result[i - 1] + diff[i];
        }
        return result;
    }
};

class DifferenceArray2D {
private:
    vector<vector<long long>> diff;
    int rows, cols;
    
public:
    DifferenceArray2D(int r, int c) : rows(r), cols(c) {
        diff.assign(r + 1, vector<long long>(c + 1, 0));
    }
    
    // 在矩形[r1,c1]到[r2,c2]上加上value
    void rangeAdd(int r1, int c1, int r2, int c2, int value) {
        diff[r1][c1] += value;
        diff[r1][c2 + 1] -= value;
        diff[r2 + 1][c1] -= value;
        diff[r2 + 1][c2 + 1] += value;
    }
    
    // 获取最终二维数组
    vector<vector<long long>> getArray() {
        vector<vector<long long>> result(rows, vector<long long>(cols));
        
        // 计算前缀和
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                result[i][j] = diff[i][j];
                if (i > 0) result[i][j] += result[i-1][j];
                if (j > 0) result[i][j] += result[i][j-1];
                if (i > 0 && j > 0) result[i][j] -= result[i-1][j-1];
            }
        }
        
        return result;
    }
};

int main() {
    // 测试一维差分数组
    cout << "一维差分数组测试:" << endl;
    vector<int> arr = {1, 2, 3, 4, 5};
    DifferenceArray1D diff1d(arr);
    
    diff1d.rangeAdd(1, 3, 10);  // 在索引1-3上加10
    diff1d.rangeAdd(0, 2, 5);   // 在索引0-2上加5
    
    vector<long long> result1d = diff1d.getArray();
    cout << "结果:";
    for (long long x : result1d) cout << x << " ";
    cout << endl;
    
    // 测试二维差分数组
    cout << "\n二维差分数组测试:" << endl;
    DifferenceArray2D diff2d(3, 3);
    
    diff2d.rangeAdd(0, 0, 1, 1, 5);  // 在2x2矩形上加5
    diff2d.rangeAdd(1, 1, 2, 2, 3);  // 在2x2矩形上加3
    
    vector<vector<long long>> result2d = diff2d.getArray();
    cout << "结果:" << endl;
    for (auto& row : result2d) {
        for (long long x : row) cout << x << " ";
        cout << endl;
    }
    
    return 0;
}

差分数组将区间更新转换为点更新。一维版本使用diff[l] += val, diff[r+1] -= val。二维版本使用容斥原理进行矩形更新。

稀疏表(ST)用于区间最值查询

稀疏表使用二进制提升技术,通过O(n log n)预处理实现O(1)时间的区间最值查询。

稀疏表实现

#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;

class SparseTable {
private:
    vector<vector<int>> st;
    vector<int> logs;
    int n;
    
public:
    SparseTable(vector<int>& arr) : n(arr.size()) {
        int maxLog = log2(n) + 1;
        st.assign(maxLog, vector<int>(n));
        logs.resize(n + 1);
        
        // 预计算对数
        logs[1] = 0;
        for (int i = 2; i <= n; i++) {
            logs[i] = logs[i / 2] + 1;
        }
        
        // 初始化第一层
        for (int i = 0; i < n; i++) {
            st[0][i] = arr[i];
        }
        
        // 构建稀疏表
        for (int k = 1; k < maxLog; k++) {
            for (int i = 0; i + (1 << k) <= n; i++) {
                st[k][i] = min(st[k-1][i], st[k-1][i + (1 << (k-1))]);
            }
        }
    }
    
    // 查询区间[l, r]的最小值
    int query(int l, int r) {
        int k = logs[r - l + 1];
        return min(st[k][l], st[k][r - (1 << k) + 1]);
    }
};

// 二进制提升求LCA
class LCA {
private:
    vector<vector<int>> up;
    vector<int> depth;
    int n, LOG;
    
    void dfs(int v, int p, const vector<vector<int>>& adj) {
        up[0][v] = p;
        for (int i = 1; i < LOG; i++) {
            up[i][v] = up[i-1][up[i-1][v]];
        }
        
        for (int u : adj[v]) {
            if (u != p) {
                depth[u] = depth[v] + 1;
                dfs(u, v, adj);
            }
        }
    }
    
public:
    LCA(int vertices, vector<vector<int>>& adj, int root = 0) : n(vertices) {
        LOG = ceil(log2(n)) + 1;
        up.assign(LOG, vector<int>(n));
        depth.assign(n, 0);
        
        dfs(root, root, adj);
    }
    
    int lca(int a, int b) {
        if (depth[a] < depth[b]) swap(a, b);
        
        // 将a提升到与b相同的层级
        int diff = depth[a] - depth[b];
        for (int i = 0; i < LOG; i++) {
            if ((diff >> i) & 1) {
                a = up[i][a];
            }
        }
        
        if (a == b) return a;
        
        // 二进制提升找LCA
        for (int i = LOG - 1; i >= 0; i--) {
            if (up[i][a] != up[i][b]) {
                a = up[i][a];
                b = up[i][b];
            }
        }
        
        return up[0][a];
    }
    
    int distance(int a, int b) {
        return depth[a] + depth[b] - 2 * depth[lca(a, b)];
    }
};

int main() {
    // 测试稀疏表
    cout << "稀疏表测试:" << endl;
    vector<int> arr = {7, 2, 3, 0, 5, 10, 3, 12, 18};
    SparseTable st(arr);
    
    cout << "数组:";
    for (int x : arr) cout << x << " ";
    cout << endl;
    
    cout << "RMQ(1, 4) = " << st.query(1, 4) << endl;
    cout << "RMQ(0, 8) = " << st.query(0, 8) << endl;
    cout << "RMQ(3, 6) = " << st.query(3, 6) << endl;
    
    // 测试LCA
    cout << "\nLCA测试:" << endl;
    vector<vector<int>> tree(7);
    tree[0] = {1, 2};
    tree[1] = {0, 3, 4};
    tree[2] = {0, 5, 6};
    tree[3] = {1};
    tree[4] = {1};
    tree[5] = {2};
    tree[6] = {2};
    
    LCA lca(7, tree, 0);
    
    cout << "LCA(3, 4) = " << lca.lca(3, 4) << endl;
    cout << "LCA(3, 5) = " << lca.lca(3, 5) << endl;
    cout << "距离(3, 5) = " << lca.distance(3, 5) << endl;
    
    return 0;
}

稀疏表利用任何区间都可以被两个重叠的2的幂覆盖这一事实。LCA使用二进制提升在树上高效跳跃,预处理所有2^k祖先。