【XCPC模板整理 - 第一期】数据结构



$\huge \boxed{\pmb {\mathfrak{I\ took\ the\ one\ less\ traveled\ by,}}}$
$\huge \boxed {\pmb {\mathfrak{\ and\ that\ has\ made\ all\ the\ difference.}}}$











前言

这是我个人使用的一些模板封装。

限于个人能力,可能存在诸多不足与漏洞,在未加测试直接使用前请务必小心谨慎。更新可能会滞后于我本地的文档,如有疑问或者催更之类的可以在评论区留言。

全文模板测试均基于以下版本信息,请留意版本兼容问题。

Windows, 64bit
G++ (ISO C++20)
stack=268435456
开启O2优化

同时,可能还使用到了如下宏定义:

#define int long long
#define endl "\n"
using LL = long long;
using ld = long double;
using PII = pair<int, int>;
using TII = tuple<int, int, int>;



并查集部分

基础封装

通常意义上我们默认增加“路径压缩”优化,时间复杂度为:查询 \(\mathcal O (1)\) ,合并接近于 \(\mathcal O(\alpha(n))\) (这里 \(\alpha\) 代表的是反阿克曼函数,一般看作是一个极小的常数)。

struct DSU {
    vector<int> fa, p;

    DSU(int n) {
        fa.resize(n + 1);
        iota(fa.begin(), fa.end(), 0);
        p.resize(n + 1, 1);
    }
    int get(int x) {
        while (x != fa[x]) {
            x = fa[x] = fa[fa[x]];
        }
        return x;
    }
    bool merge(int x, int y) { // 设x是y的祖先
        x = get(x), y = get(y);
        if (x == y) return false;
        if (x < y) swap(x, y); // 将编号小的合并到大的上
        fa[y] = x;
        p[x] += p[y];
        return true;
    }
    bool same(int x, int y) {
        return get(x) == get(y);
    }
    int size(int x) { // 输出连通块中点的数量
        return p[get(x)];
    }
};

求解连通块内部点、边、环信息

并查集算法除了常规意义上的寻找(时间序意义上的)祖先节点、寻找块内最小/最大的节点之外,还可以用极小的时间代价维护连通块内点的数量、边的数量、是否存在自环等等内容。除了“路径压缩”优化外,还有“启发式合并”优化能够进一步缩短合并花费的时间,然而这会影响上述罗列的最大/最小节点查找,故一般我个人习惯不添加这一优化。

struct DSU {
    vector<int> fa, p, e, f;

    DSU(int n) {
        fa.resize(n + 1);
        iota(fa.begin(), fa.end(), 0);
        p.resize(n + 1, 1);
        e.resize(n + 1);
        f.resize(n + 1);
    }
    int get(int x) {
        while (x != fa[x]) {
            x = fa[x] = fa[fa[x]];
        }
        return x;
    }
    bool merge(int x, int y) { // 设x是y的祖先
        if (x == y) f[get(x)] = 1;
        x = get(x), y = get(y);
        e[x]++;
        if (x == y) return false;
        if (x < y) swap(x, y); // 将编号小的合并到大的上
        fa[y] = x;
        f[x] |= f[y], p[x] += p[y], e[x] += e[y];
        return true;
    }
    bool same(int x, int y) {
        return get(x) == get(y);
    }
    bool F(int x) { // 判断连通块内是否存在自环
        return f[get(x)];
    }
    int size(int x) { // 输出连通块中点的数量
        return p[get(x)];
    }
    int E(int x) { // 输出连通块中边的数量
        return e[get(x)];
    }
};

维护路径信息(带权并查集)

留坑。



树状数组部分

基础封装

struct BIT {
    int n;
    vector<int> w;

    BIT(int n) {
        this->n = n; // 这里必须写 n ,否则会RE
        w.resize(n + 1);
    }
    void add(int x, int k) {
        for (; x <= n; x += x & -x) {
            w[x] += k;
        }
    }
    void add(int x, int y, int k) { // 区间修改
        add(x, k), add(y, -k);
    }
    int ask(int x) {
        int ans = 0;
        for (; x; x -= x & -x) {
            ans += w[x];
        }
        return ans;
    }
    int ask(int x, int y) { // 区间查询
        return ask(y) - ask(x - 1);
    }
    int kth(int k) { // ex: 查找第k大的值
        int ans = 0;
        for (int i = __lg(n); i >= 0; i--) {
            int val = ans + (1 << i);
            if (val < n && w[val] < k) {
                k -= w[val];
                ans = val;
            }
        }
        return ans + 1;
    }
};

求解逆序对

struct BIT {
    int n;
    vector<int> w;

    BIT() {}
    void add(int x, int k) {
        for (; x <= n; x += x & -x) {
            w[x] += k;
        }
    }
    int ask(int x) {
        int ans = 0;
        for (; x; x -= x & -x) {
            ans += w[x];
        }
        return ans;
    }
    int ask(int x, int y) {
        return ask(y) - ask(x - 1);
    }
    int get(auto val) { // 获取逆序对数量
        this->n = val.size() - 1; // 注意 n 不能 +1
        w.resize(n + 1);

        vector<pair<int, int>> alls;
        for (int i = 1; i <= n; i++) {
            alls.emplace_back(val[i], i);
        }
        sort(alls.begin(), alls.end());

        int ans = 0;
        for (auto [val, idx] : alls) {
            ans += ask(idx + 1, n);
            add(idx, 1);
        }
        return ans;
    }
};

树状数组套树状数组(二维树状数组)1

请注意,该版本不能同时进行区间修改+区间查询。无离散化版本的空间占用为 \(\mathcal O(NM)\) 、建树复杂度为 \(\mathcal O(NM)\) 、单次查询复杂度为 \(\mathcal O(\log N\cdot \log M)\)

大致有以下两种写法,均通过模板题测试。

该部分模板题可参考:LOJ #133. 二维树状数组 1:单点修改,区间查询LOJ #134. 二维树状数组 2:区间修改,单点查询

借助一维树状数组进行拓展

struct BIT_2D {
    struct BIT {
        int n;
        vector<int> w;
        BIT() {}
        BIT(int n) {
            this->n = n; // 这里必须写 n ,否则会RE
            w.resize(n + 1);
        }
        void add(int x, int k) {
            for (; x <= n; x += x & -x) {
                w[x] += k;
            }
        }
        int ask(int x) {
            int ans = 0;
            for (; x; x -= x & -x) {
                ans += w[x];
            }
            return ans;
        }
    };
    int n;
    vector<BIT> w;

    BIT_2D() {}
    BIT_2D(int n, int m) {
        this->n = n;
        w.resize(n + 1);
        for (int i = 1; i <= n; i++) {
            w[i] = BIT(m);
        }
    }
    void add(int x, int y, int k) {
        for (; x <= n; x += x & -x) {
            w[x].add(y, k);
        }
    }
    void add(int x, int y, int X, int Y, int k) { // 区块修改:二维差分
        X++, Y++;
        add(x, y, k), add(X, y, -k);
        add(X, Y, k), add(x, Y, -k);
    }
    int ask(int x, int y) { // 单点查询
        int ans = 0;
        for (; x; x -= x & -x) {
            ans += w[x].ask(y);
        }
        return ans;
    }
    int ask(int x, int y, int X, int Y) { // 区块查询:二维前缀和
        x--, y--;
        return ask(X, Y) - ask(x, Y) - ask(X, y) + ask(x, y);
    }
};

压缩优化

struct BIT_2D {
    int n, m;
    vector<vector<int>> w;
    
    BIT_2D(int n, int m) : n(n), m(m) {
        w.resize(n + 1, vector<int>(m + 1));
    }
    void add(int x, int y, int k) {
        for (int i = x; i <= n; i += i & -i) {
            for (int j = y; j <= m; j += j & -j) {
                w[i][j] += k;
            }
        }
    }
    void add(int x, int y, int X, int Y, int k) { // 区块修改:二维差分
        X++, Y++;
        add(x, y, k), add(X, y, -k);
        add(X, Y, k), add(x, Y, -k);
    }
    int ask(int x, int y) { // 单点查询
        int ans = 0;
        for (int i = x; i; i -= i & -i) {
            for (int j = y; j; j -= j & -j) {
                ans += w[i][j];
            }
        }
        return ans;
    }
    int ask(int x, int y, int X, int Y) { // 区块查询:二维前缀和
        x--, y--;
        return ask(X, Y) - ask(x, Y) - ask(X, y) + ask(x, y);
    }
};

树状数组套树状数组(二维树状数组)2

该版本支持全部操作。但是时空复杂度均比上一个版本多 \(4\) 倍。

这里仅提供压缩优化版。

该部分模板题可参考:LOJ #135. 二维树状数组 3:区间修改,区间查询

struct BIT_2D {
    int n, m;
    vector<vector<int>> b1, b2, b3, b4;
    
    BIT_2D(int n, int m) : n(n), m(m) {
        b1.resize(n + 1, vector<int>(m + 1));
        b2.resize(n + 1, vector<int>(m + 1));
        b3.resize(n + 1, vector<int>(m + 1));
        b4.resize(n + 1, vector<int>(m + 1));
    }
    void add(auto &w, int x, int y, int k) { // 单点修改
        for (int i = x; i <= n; i += i & -i) {
            for (int j = y; j <= m; j += j & -j) {
                w[i][j] += k;
            }
        }
    }
    void add(int x, int y, int k) { // 多了一步计算
        add(b1, x, y, k);
        add(b2, x, y, k * (x - 1));
        add(b3, x, y, k * (y - 1));
        add(b4, x, y, k * (x - 1) * (y - 1));
    }
    void add(int x, int y, int X, int Y, int k) { // 区块修改:二维差分
        X++, Y++;
        add(x, y, k), add(X, y, -k);
        add(X, Y, k), add(x, Y, -k);
    }
    int ask(auto &w, int x, int y) { // 单点查询
        int ans = 0;
        for (int i = x; i; i -= i & -i) {
            for (int j = y; j; j -= j & -j) {
                ans += w[i][j];
            }
        }
        return ans;
    }
    int ask(int x, int y) { // 多了一步计算
        int ans = 0;
        ans += x * y * ask(b1, x, y);
        ans -= y * ask(b2, x, y);
        ans -= x * ask(b3, x, y);
        ans += ask(b4, x, y);
        return ans;
    }
    int ask(int x, int y, int X, int Y) { // 区块查询:二维前缀和
        x--, y--;
        return ask(X, Y) - ask(x, Y) - ask(X, y) + ask(x, y);
    }
};

树状数组套树状数组(二维树状数组)3

第一个版本加上了离散化之后的效果,可以用来解决 这一道二维树状数组的题目,但是有一些细节我还没研究清楚,暂时留坑。

我的提交

struct BIT_2D {
    struct Compress {
        int n;
        vector<int> alls;
        
        Compress() {}
        
        void add(int x) {
            alls.emplace_back(x);
        }
        
        void init() {
            alls.emplace_back(numeric_limits<int>::max());
            sort(alls.begin(), alls.end());
            alls.erase(unique(alls.begin(), alls.end()), alls.end());
            this->n = alls.size();
        }
        
        int operator[](int x) { // 返回 x 元素的新下标
            return upper_bound(alls.begin(), alls.end(), x) - alls.begin();
        }
        
        int get(int x) { // 返回 x 元素的新下标
            return lower_bound(alls.begin(), alls.end(), x) - alls.begin();
        }
    };
    int n;
    vector<vector<int>> w;
    vector<pair<int, int>> ver;
    
    Compress row;
    vector<Compress> col;
    
    BIT_2D() {}
    void merge(int x, int y) {
        ver.emplace_back(x, y);
    }
    void init() {
        for (auto [x, y] : ver) { // 离散化横坐标
            row.add(x);
        }
        row.init();
        
        this->n = row.n;
        w.resize(n + 1);
        col.resize(n + 1);
        
        for (auto [x, y] : ver) { // 对每一行的纵坐标离散化,以此节约空间
            for (int i = row[x]; i <= n; i += i & -i) {
                col[i].add(y);
            }
        }
        
        for (int i = 1; i <= n; i++) {
            col[i].init();
            w[i].resize(col[i].n);
        }
    }
    
    void add(int x, int y, int k) {
        for (int i = row[x]; i <= n; i += i & -i) {
            for (int j = col[i][y]; j < col[i].n; j += j & -j) {
                w[i][j] += k;
            }
        }
    }
    void add(int x, int y, int X, int Y, int k) { // 区块修改:二维差分
        X++, Y++;
        add(x, y, k), add(X, y, -k);
        add(X, Y, k), add(x, Y, -k);
    }
    int ask(int x, int y) { // 单点查询
        int ans = 0;
        for (int i = row[x]; i; i -= i & -i) {
            for (int j = col[i][y]; j; j -= j & -j) {
                ans += w[i][j];
            }
        }
        return ans;
    }
    int ask(int x, int y, int X, int Y) { // 区块查询:二维前缀和
        x--, y--;
        return ask(X, Y) - ask(x, Y) - ask(X, y) + ask(x, y);
    }
    
    void debug() {
        for (int i = 1; i <= 20; i++) {
            for (int j = 1; j <= 20; j++) {
                cout << setw(3) << ask(i, j) << " ";
            }
            cout << endl;
        }
    }
};



取模类

集成了常见的取模四则运算,运算速度与手动取模相差无几,效率极高。包含以下特点:

  • 模数可为输入的数字(不需要确保是静态变量);
  • 可以与 #define int long long 同时存在。

目前存在以下缺陷暂时没办法解决:

  • 多个可变模数;
  • 支持 long long 类型(Jiangly那边的最新封装可以解决这一问题);
  • 支持 x++; 操作。
// Zmod专用快速幂,如需使用,请注意相应的类型转换:如mypow((Z)2, k)
template <class T> T mypow(T n, int k, T r = 1) {
    for (; k; k >>= 1, n *= n) {
        if (k & 1) r *= n;
    }
    return r;
}

int MOD = 998244353;
struct Z {
    int x;
    Z(int x = 0) : x(norm(x % MOD)) {}
    int val() const {
        return x;
    }
    int norm(int x) const {
        return (x + MOD) % MOD;
    }
    Z operator-() const {
        int val = norm(MOD - x);
        return Z(val);
    }
    Z inv() const {
        assert(x != 0);
        return mypow(*this, MOD - 2);
    }
    Z &operator*=(const Z &i) {
        return x = x * i.x % MOD, *this;
    }
    Z &operator+=(const Z &i) {
        return x = norm(x + i.x), *this;
    }
    Z &operator-=(const Z &i) {
        return x = norm(x - i.x), *this;
    }
    Z &operator/=(const Z &i) {
        return *this *= i.inv();
    }
    Z &operator%=(const int &i) {
        return x %= i, *this;
    }
    friend Z operator*(const Z &i, const Z &j) {
        Z res = i;
        res *= j;
        return res;
    }
    friend Z operator+(const Z &i, const Z &j) {
        Z res = i;
        res += j;
        return res;
    }
    friend Z operator-(const Z &i, const Z &j) {
        Z res = i;
        res -= j;
        return res;
    }
    friend Z operator/(const Z &i, const Z &j) {
        Z res = i;
        res /= j;
        return res;
    }
    friend Z operator%(const Z &i, const int &j) {
        Z res = i;
        res %= j;
        return res;
    }
    friend auto &operator>>(istream &it, Z &j) {
        int v;
        it >> v;
        j = Z(v);
        return it;
    }
    friend auto &operator<<(ostream &o, const Z &j) {
        return o << j.x;
    }
    bool operator<(const Z &i) const {
        return x < i.x;
    }
    bool operator>(const Z &i) const {
        return x > i.x;
    }
    bool operator==(const Z &i) const {
        return x == i.x;
    }
    bool operator!=(const Z &i) const {
        return x != i.x;
    }
};



线段树部分

标准封装(区间加法+区间最小值+区间合并)

由于有时候会取模,故采用了可变封装。

template <class T> struct Segt_ {
    struct node {
        int l, r;
        T w, Min;
        T lazy;
    };
    vector<T> w;
    vector<node> t;

    Segt_(int n) {
        w.resize(n + 1);
        t.resize((n << 2) + 1);
        build(1, n);
    }
    Segt_(vector<int> in) {
        int n = in.size() - 1;
        w.resize(n + 1);
        for (int i = 1; i <= n; i++) {
            w[i] = in[i];
        }
        t.resize((n << 2) + 1);
        build(1, n);
    }
    void pushdown(node &p, T lazy) { // 在此更新下递函数
        p.w += (p.r - p.l + 1) * lazy;
        p.Min += lazy;
        p.lazy += lazy;
    }
    void pushup(node &p, node &l, node &r) { // 在此更新上传函数
        p.w = l.w + r.w;
        p.Min = min(l.Min, r.Min);
    }
#define GL (k << 1)
#define GR (k << 1 | 1)
    void pushdown(int k) { // 不需要动
        if (t[k].lazy == 0) return;
        pushdown(t[GL], t[k].lazy);
        pushdown(t[GR], t[k].lazy);
        t[k].lazy = 0;
    }
    void pushup(int k) { // 不需要动
        pushup(t[k], t[GL], t[GR]);
    }
    void build(int l, int r, int k = 1) {
        if (l == r) {
            t[k] = {l, r, w[l], w[l]};
            return;
        }
        t[k] = {l, r};
        int mid = (l + r) / 2;
        build(l, mid, GL);
        build(mid + 1, r, GR);
        pushup(k);
    }
    void modify(int l, int r, T val, int k = 1) { // 区间修改
        if (l <= t[k].l && t[k].r <= r) {
            pushdown(t[k], val);
            return;
        }
        pushdown(k);
        int mid = (t[k].l + t[k].r) / 2;
        if (l <= mid) modify(l, r, val, GL);
        if (mid < r) modify(l, r, val, GR);
        pushup(k);
    }
    T Min(int l, int r, int k = 1) { // 区间询问最小值
        if (l <= t[k].l && t[k].r <= r) {
            return t[k].Min;
        }
        pushdown(k);
        int mid = (t[k].l + t[k].r) / 2;
        T ans = 1E18;
        if (l <= mid) ans = min(ans, Min(l, r, GL));
        if (mid < r) ans = min(ans, Min(l, r, GR));
        return ans;
    }
    T ask(int l, int r, int k = 1) { // 区间询问,不合并
        if (l <= t[k].l && t[k].r <= r) {
            return t[k].w;
        }
        pushdown(k);
        int mid = (t[k].l + t[k].r) / 2;
        T ans = 0;
        if (l <= mid) ans += ask(l, r, GL);
        if (mid < r) ans += ask(l, r, GR);
        return ans;
    }
    node Ask(int l, int r, int k = 1) { // 区间合并
        if (l <= t[k].l && t[k].r <= r) {
            return t[k];
        }
        pushdown(k);
        int mid = (t[k].l + t[k].r) / 2;
        // 区间合并这里的if是反过来的
        if (r <= mid) return Ask(l, r, GL);
        if (mid < l) return Ask(l, r, GR);
        auto left = Ask(l, r, GL), right = Ask(l, r, GR);
        node res = {0, 0, 0, 0};
        pushup(res, left, right); // 合并left和right为新区间
        return res;
    }
    void debug(int k = 1) {
        cout << "[" << t[k].l << ", " << t[k].r << "]: ";
        cout << "w = " << t[k].w << ", ";
        cout << "Min = " << t[k].Min << ", ";
        cout << "lazy = " << t[k].lazy << ", ";
        cout << endl;
        if (t[k].l == t[k].r) return;
        debug(GL), debug(GR);
    }
#undef GL
#undef GR
};
using Segt = Segt_<int>;

同时需要处理区间加法与乘法修改

该部分模板题可参考:洛谷P3373 【模板】线段树 2,难点在于懒标记的优先级问题。

template <class T> struct Segt_ {
    struct node {
        int l, r;
        T w, add, mul = 1; // 注意初始赋值
    };
    vector<T> w;
    vector<node> t;

    Segt_(int n) {
        w.resize(n + 1);
        t.resize((n << 2) + 1);
        build(1, n);
    }
    Segt_(vector<int> in) {
        int n = in.size() - 1;
        w.resize(n + 1);
        for (int i = 1; i <= n; i++) {
            w[i] = in[i];
        }
        t.resize((n << 2) + 1);
        build(1, n);
    }
    void pushdown(node &p, T add, T mul) { // 在此更新下递函数
        p.w = p.w * mul + (p.r - p.l + 1) * add;
        p.add = p.add * mul + add;
        p.mul *= mul;
    }
    void pushup(node &p, node &l, node &r) { // 在此更新上传函数
        p.w = l.w + r.w;
    }
#define GL (k << 1)
#define GR (k << 1 | 1)
    void pushdown(int k) { // 不需要动
        pushdown(t[GL], t[k].add, t[k].mul);
        pushdown(t[GR], t[k].add, t[k].mul);
        t[k].add = 0, t[k].mul = 1;
    }
    void pushup(int k) { // 不需要动
        pushup(t[k], t[GL], t[GR]);
    }
    void build(int l, int r, int k = 1) {
        if (l == r) {
            t[k] = {l, r, w[l]};
            return;
        }
        t[k] = {l, r};
        int mid = (l + r) / 2;
        build(l, mid, GL);
        build(mid + 1, r, GR);
        pushup(k);
    }
    void modify(int l, int r, T val, int k = 1) { // 区间修改
        if (l <= t[k].l && t[k].r <= r) {
            t[k].w += (t[k].r - t[k].l + 1) * val;
            t[k].add += val;
            return;
        }
        pushdown(k);
        int mid = (t[k].l + t[k].r) / 2;
        if (l <= mid) modify(l, r, val, GL);
        if (mid < r) modify(l, r, val, GR);
        pushup(k);
    }
    void modify2(int l, int r, T val, int k = 1) { // 区间修改
        if (l <= t[k].l && t[k].r <= r) {
            t[k].w *= val;
            t[k].add *= val;
            t[k].mul *= val;
            return;
        }
        pushdown(k);
        int mid = (t[k].l + t[k].r) / 2;
        if (l <= mid) modify2(l, r, val, GL);
        if (mid < r) modify2(l, r, val, GR);
        pushup(k);
    }
    T ask(int l, int r, int k = 1) { // 区间询问,不合并
        if (l <= t[k].l && t[k].r <= r) {
            return t[k].w;
        }
        pushdown(k);
        int mid = (t[k].l + t[k].r) / 2;
        T ans = 0;
        if (l <= mid) ans += ask(l, r, GL);
        if (mid < r) ans += ask(l, r, GR);
        return ans;
    }
    void debug(int k = 1) {
        cout << "[" << t[k].l << ", " << t[k].r << "]: ";
        cout << "w = " << t[k].w << ", ";
        cout << "add = " << t[k].add << ", ";
        cout << "mul = " << t[k].mul << ", ";
        cout << endl;
        if (t[k].l == t[k].r) return;
        debug(GL), debug(GR);
    }
#undef GL
#undef GR
};

扫描线(面积并、周长并)

暂未封装,留坑。

线段树套线段树(二维线段树、矩形树)

暂未封装,留坑。

主席树(可持久化权值线段树)

\(\mathcal O(N\log N)\) 的时间复杂度建树、查询、修改。下方代码为查询区间第 \(k\) 小值的模板。这部分的代码来自我的队友 \(\pmb{Hamine}\)

struct PresidentTree {
    static constexpr int N = 2e5 + 10;
    int cntNodes, root[N];
    struct node {
        int l, r;
        int cnt;
    } tr[4 * N + 17 * N];
    void modify(int &u, int v, int l, int r, int x) {
        u = ++cntNodes;
        tr[u] = tr[v];
        tr[u].cnt++;
        if (l == r) return;
        int mid = (l + r) / 2;
        if (x <= mid)
            modify(tr[u].l, tr[v].l, l, mid, x);
        else
            modify(tr[u].r, tr[v].r, mid + 1, r, x);
    }
    int kth(int u, int v, int l, int r, int k) {
        if (l == r) return l;
        int res = tr[tr[v].l].cnt - tr[tr[u].l].cnt;
        int mid = (l + r) / 2;
        if (k <= res)
            return kth(tr[u].l, tr[v].l, l, mid, k);
        else
            return kth(tr[u].r, tr[v].r, mid + 1, r, k - res);
    }
};



离散化与坐标压缩

基础封装

template <typename T> struct Compress_ {
    int n, shift = 0; // shift 用于标记下标偏移量
    vector<T> alls;

    Compress_() {}
    Compress_(auto in) : alls(in) {
        init();
    }
    void add(T x) {
        alls.emplace_back(x);
    }
    template <typename... Args> void add(T x, Args... args) {
        add(x), add(args...);
    }
    void init() {
        alls.emplace_back(numeric_limits<T>::max());
        sort(alls.begin(), alls.end());
        alls.erase(unique(alls.begin(), alls.end()), alls.end());
        this->n = alls.size();
    }
    int size() {
        return n;
    }
    int operator[](T x) { // 返回 x 元素的新下标
        return upper_bound(alls.begin(), alls.end(), x) - alls.begin() + shift;
    }
    T Get(int x) { // 根据新下标返回原来元素
        assert(x - shift < n);
        return x - shift < n ? alls[x - shift] : -1;
    }
    bool count(T x) { // 查找元素 x 是否存在
        return binary_search(alls.begin(), alls.end(), x);
    }
    friend auto &operator<<(ostream &o, const auto &j) {
        cout << "{";
        for (auto it : j.alls) {
            o << it << " ";
        }
        return o << "}";
    }
};
using Compress = Compress_<int>;

未封装简洁版

sort(alls.begin(), alls.end());
alls.erase(unique(alls.begin(), alls.end()), alls.end());
auto get = [&](int x) {
    return lower_bound(alls.begin(), alls.end(), x) - alls.begin();
};



莫队部分

普通莫队

\(\mathcal O(N \sqrt N)\) 的复杂度完成 \(Q\) 次询问的离线查询,其中每个分块的大小取 \(\sqrt N=\sqrt {10^5} = 317\) ,也可以使用 ceil((double)n / (int)sqrt(n)) 或者 sqrt(n) 划分。

signed main() {
    int n;
    cin >> n;
    
    vector<int> w(n + 1);
    for (int i = 1; i <= n; i++) {
        cin >> w[i];
    }
    
    int q;
    cin >> q;
    vector<array<int, 3>> query(q + 1);
    for (int i = 1; i <= q; i++) {
        int l, r;
        cin >> l >> r;
        query[i] = {l, r, i};
    }
    
    int Knum = n / min<int>(n, sqrt(q)); // 计算块长
    vector<int> K(n + 1);
    for (int i = 1; i <= n; i++) { // 固定块长
        K[i] = (i - 1) / Knum + 1;
    }
    sort(query.begin() + 1, query.end(), [&](auto x, auto y) {
        if (K[x[0]] != K[y[0]]) return x[0] < y[0];
        if (K[x[0]] & 1) return x[1] < y[1];
        return x[1] > y[1];
    });
    
    int l = 1, r = 0, val = 0;
    vector<int> ans(q + 1);
    for (int i = 1; i <= q; i++) {
        auto [ql, qr, id] = query[i];
        
        auto add = [&](int x) -> void {
            
        };
        
        auto del = [&](int x) -> void {
            
        };
        
        while (l > ql) add(w[--l]);
        while (r < qr) add(w[++r]);
        while (l < ql) del(w[l++]);
        while (r > qr) del(w[r--]);
        ans[id] = val;
    }
    
    for (int i = 1; i <= q; i++) {
        cout << ans[i] << endl;
    }
}

带修莫队(带时间维度的莫队)

\(\mathcal O(N^\frac{5}{3})\) 的复杂度完成 \(Q\) 次询问的离线查询,其中每个分块的大小取 \(N^\frac{2}{3}=\sqrt[3]{100000^2}=2154\) (直接取会略快),也可以使用 pow(n, 0.6666) 划分。

signed main() {
    int n, q;
    cin >> n >> q;
    
    vector<int> w(n + 1);
    for (int i = 1; i <= n; i++) {
        cin >> w[i];
    }
    
    vector<array<int, 4>> query = {{}}; // {左区间, 右区间, 累计修改次数, 下标}
    vector<array<int, 2>> modify = {{}}; // {修改的值, 修改的元素下标}
    for (int i = 1; i <= q; i++) {
        char op;
        cin >> op;
        if (op == 'Q') {
            int l, r;
            cin >> l >> r;
            query.push_back({l, r, (int)modify.size() - 1, (int)query.size()});
        } else {
            int idx, w;
            cin >> idx >> w;
            modify.push_back({w, idx});
        }
    }
    
    int Knum = 2154; // 计算块长
    vector<int> K(n + 1);
    for (int i = 1; i <= n; i++) { // 固定块长
        K[i] = (i - 1) / Knum + 1;
    }
    sort(query.begin() + 1, query.end(), [&](auto x, auto y) {
        if (K[x[0]] != K[y[0]]) return x[0] < y[0];
        if (K[x[1]] != K[y[1]]) return x[1] < y[1];
        return x[3] < y[3];
    });
    
    int l = 1, r = 0, val = 0;
    int t = 0; // 累计修改次数
    vector<int> ans(query.size());
    for (int i = 1; i < query.size(); i++) {
        auto [ql, qr, qt, id] = query[i];
        
        auto add = [&](int x) -> void {

        };
        
        auto del = [&](int x) -> void {

        };
        
        auto time = [&](int x, int l, int r) -> void {
            
        };
        
        while (l > ql) add(w[--l]);
        while (r < qr) add(w[++r]);
        while (l < ql) del(w[l++]);
        while (r > qr) del(w[r--]);
        while (t < qt) time(++t, ql, qr);
        while (t > qt) time(t--, ql, qr);
        ans[id] = val;
    }
    
    for (int i = 1; i < ans.size(); i++) {
        cout << ans[i] << endl;
    }
}



树链剖分/轻重链剖分

基础封装(配合线段树等数据结构使用)

本封装将线段树处理的部分分离,方便修改。支持模板题P3384 【模板】重链剖分/树链剖分的四个查询(链上查询/修改、子树查询/修改),建树时间复杂度 \(\mathcal O(N\log N)\) ,单次查询时间复杂度 \(\mathcal O(\log ^2 N)\)

struct HLD {
    int n, idx;
    vector<vector<int>> ver;
    vector<int> siz, dep;
    vector<int> top, son, parent;
    vector<int> in, id, val;
    Segt segt;

    HLD(int n) {
        this->n = n;
        ver.resize(n + 1);
        siz.resize(n + 1);
        dep.resize(n + 1);

        top.resize(n + 1);
        son.resize(n + 1);
        parent.resize(n + 1);

        idx = 0;
        id.resize(n + 1);
        val.resize(n + 1);
    }
    void add(int x, int y) { // 建立双向边
        ver[x].push_back(y);
        ver[y].push_back(x);
    }
    void dfs1(int x) {
        siz[x] = 1;
        dep[x] = dep[parent[x]] + 1;
        for (auto y : ver[x]) {
            if (y == parent[x]) continue;
            parent[y] = x;
            dfs1(y);
            siz[x] += siz[y];
            if (siz[y] > siz[son[x]]) {
                son[x] = y;
            }
        }
    }
    void dfs2(int x, int up) {
        id[x] = ++idx;
        val[idx] = in[x]; // 建立编号
        top[x] = up;
        if (son[x]) dfs2(son[x], up);
        for (auto y : ver[x]) {
            if (y == parent[x] || y == son[x]) continue;
            dfs2(y, y);
        }
    }
    void work(int root, auto in) { // 在此初始化
        this->in = in;

        dfs1(root);
        dfs2(root, root);

        segt.init(val); // 建立线段树
    }

    void modify(int l, int r, int val) { // 链上修改
        while (top[l] != top[r]) {
            if (dep[top[l]] < dep[top[r]]) {
                swap(l, r);
            }
            segt.modify(id[top[l]], id[l], val);
            l = parent[top[l]];
        }
        if (dep[l] > dep[r]) {
            swap(l, r);
        }
        segt.modify(id[l], id[r], val);
    }
    void modify(int root, int val) { // 子树修改
        segt.modify(id[root], id[root] + siz[root] - 1, val);
    }
    int ask(int l, int r) { // 链上查询
        int ans = 0;
        while (top[l] != top[r]) {
            if (dep[top[l]] < dep[top[r]]) {
                swap(l, r);
            }
            ans += segt.ask(id[top[l]], id[l]);
            l = parent[top[l]];
        }
        if (dep[l] > dep[r]) {
            swap(l, r);
        }
        return ans + segt.ask(id[l], id[r]);
    }
    int ask(int root) { // 子树查询
        return segt.ask(id[root], id[root] + siz[root] - 1);
    }
};

求解最近公共祖先

预处理时间复杂度 \(\mathcal O(N)\) ;单次查询 \(\mathcal O(\log N)\) ,比树上倍增法求解最近公共祖先常数更小。

struct HLD {
    int n, idx;
    vector<vector<int>> ver;
    vector<int> siz, dep;
    vector<int> top, son, parent;

    HLD(int n) {
        this->n = n;
        ver.resize(n + 1);
        siz.resize(n + 1);
        dep.resize(n + 1);

        top.resize(n + 1);
        son.resize(n + 1);
        parent.resize(n + 1);
    }
    void add(int x, int y) { // 建立双向边
        ver[x].push_back(y);
        ver[y].push_back(x);
    }
    void dfs1(int x) {
        siz[x] = 1;
        dep[x] = dep[parent[x]] + 1;
        for (auto y : ver[x]) {
            if (y == parent[x]) continue;
            parent[y] = x;
            dfs1(y);
            siz[x] += siz[y];
            if (siz[y] > siz[son[x]]) {
                son[x] = y;
            }
        }
    }
    void dfs2(int x, int up) {
        top[x] = up;
        if (son[x]) dfs2(son[x], up);
        for (auto y : ver[x]) {
            if (y == parent[x] || y == son[x]) continue;
            dfs2(y, y);
        }
    }
    void work(int root) { // 在此初始化
        dfs1(root);
        dfs2(root, root);
    }

    int lca(int x, int y) {
        while (top[x] != top[y]) {
            if (dep[top[x]] > dep[top[y]]) {
                x = parent[top[x]];
            } else {
                y = parent[top[y]];
            }
        }
        return dep[x] < dep[y] ? x : y;
    }
    int clac(int x, int y) { // 查询两点间距离
        return dep[x] + dep[y] - 2 * dep[lca(x, y)];
    }
};



分数运算类

定义了分数的四则运算,如果需要处理浮点数,那么需要将函数中的 gcd 运算替换为 fgcd

template<class T> struct Frac {
    T x, y;
    Frac(T x_, T y_) : x(x_), y(y_) {
        if (y < 0) {
            y = -y;
            x = -x;
        }
    }
    Frac() : Frac(0, 1) {}
    Frac(T x_) : Frac(x_, 1) {}
    constexpr long double val() {
        return (long double)(x) / y;
    }
    constexpr Frac norm() { // 调整符号、转化为最简形式
        T p = gcd(x, y);
        return {x / p, y / p};
    }
    Frac &operator+=(const Frac &it) {
        x = x * it.y + it.x * y;
        y *= it.y;
        return *this;
    }
    Frac &operator-=(const Frac &it) {
        x = x * it.y - it.x * y;
        y *= it.y;
        return *this;
    }
    Frac &operator*=(const Frac &it) {
        x *= it.x;
        y *= it.y;
        return *this;
    }
    Frac &operator/=(const Frac &it) {
        x *= it.y;
        y *= it.x;
        if (y < 0) {
            x = -x;
            y = -y;
        }
        return *this;
    }
    friend Frac operator+(Frac ans, const Frac &it) { return ans += it; }
    friend Frac operator-(Frac ans, const Frac &it) { return ans -= it; }
    friend Frac operator*(Frac ans, const Frac &it) { return ans *= it; }
    friend Frac operator/(Frac ans, const Frac &it) { return ans /= it; }
    friend Frac operator-(const Frac &ans) { return Frac(-ans.x, ans.y); }
    friend bool operator<(const Frac &ans, const Frac &it) {
        return ans.x * it.y < it.x * ans.y;
    }
    friend bool operator>(const Frac &ans, const Frac &it) {
        return ans.x * it.y > it.x * ans.y;
    }
    friend bool operator==(const Frac &ans, const Frac &it) {
        return ans.x * it.y == it.x * ans.y;
    }
    friend bool operator!=(const Frac &ans, const Frac &it) {
        return ans.x * it.y != it.x * ans.y;
    }
    friend std::ostream &operator<<(std::ostream &os, Frac it) {
        T p = fgcd(it.x, it.y);
        if (it.y == p) {
            return os << it.x / p;
        } else {
            return os << it.x / p << "/" << it.y / p;
        }
    }
};



平衡二叉树部分

pd_ds库

懒得手写,所以直接使用 \(\tt pd_ds\) 库实现,记得加上相应的头文件,同时需要注意定义时的参数,一般只需要修改第三个参数:即定义的是大根堆还是小根堆。

附常见成员函数:

empty() / size()
insert(x) // 插入元素x
erase(x) // 删除元素/迭代器x
order_of_key(x) // 返回元素x的排名
find_by_order(x) // 返回排名为x的元素迭代器
lower_bound(x) / upper_bound(x) // 返回迭代器
join(Tree) // 将Tree树的全部元素并入当前的树
split(x, Tree) // 将大于x的元素放入Tree树
#include <bits/extc++.h>
using namespace __gnu_pbds;
bool Solve() {
    using PII = pair<int, int>;
    tree<PII, null_type, less<PII>, rb_tree_tag, tree_order_statistics_node_update> ver;
    map<int, int> dic;
    int n; cin >> n;
    while (n--) {
        int op, x;
        cin >> op >> x;
        if (op == 1) { // 插入一个元素x,允许重复
            ver.insert({x, ++dic[x]});
        } else if (op == 2) { // 删除元素x,若有重复,则任意删除一个
            ver.erase({x, dic[x]--});
        } else if (op == 3) { // 查询元素x的排名(排名定义为比当前数小的数的个数+1)
            cout << ver.order_of_key({x, 1}) + 1 << endl;
        } else if (op == 4) { // 查询排名为x的元素
            x--;
            cout << ver.find_by_order(x)->first << endl;
        } else if (op == 5) { // 查询元素x的前驱
            int idx = ver.order_of_key({x, 1}) - 1; // 无论x存不存在,idx都代表x的位置,需要-1
            cout << ver.find_by_order(idx)->first << endl;
        } else if (op == 6) { // 查询元素x的后继
            int idx = ver.order_of_key( {x, dic[x]}); // 如果x不存在,那么idx就是x的后继
            if (ver.find({x, 1}) != ver.end()) idx++; // 如果x存在,那么idx是x的位置,需要+1
            cout << ver.find_by_order(idx)->first << endl;
        }
    }
    return 0;
}

其他

留坑。



珂朵莉树 (OD Tree)

这部分的代码来自我的队友 \(\pmb{Hamine}\)。区间赋值的数据结构都可以骗分,在数据随机的情况下,复杂度可以保证,时间复杂度:\(\mathcal O(N\log\log N)\)

struct ODT {
    struct node {
        int l, r;
        mutable LL v;
        node(int l, int r = -1, LL v = 0) : l(l), r(r), v(v) {}
        bool operator<(const node &o) const {
            return l < o.l;
        }
    };
    set<node> s;
    ODT() {
        s.clear();
    }
    auto split(int pos) {
        auto it = s.lower_bound(node(pos));
        if (it != s.end() && it->l == pos) return it;
        it--;
        int l = it->l, r = it->r;
        LL v = it->v;
        s.erase(it);
        s.insert(node(l, pos - 1, v));
        return s.insert(node(pos, r, v)).first;
    }
    void assign(int l, int r, LL x) {
        auto itr = split(r + 1), itl = split(l);
        s.erase(itl, itr);
        s.insert(node(l, r, x));
    }
    void add(int l, int r, LL x) {
        auto itr = split(r + 1), itl = split(l);
        for (auto it = itl; it != itr; it++) {
            it->v += x;
        }
    }
    LL kth(int l, int r, int k) {
        vector<pair<LL, int>> a;
        auto itr = split(r + 1), itl = split(l);
        for (auto it = itl; it != itr; it++) {
            a.push_back(pair<LL, int>(it->v, it->r - it->l + 1));
        }
        sort(a.begin(), a.end());
        for (auto [val, len] : a) {
            k -= len;
            if (k <= 0) return val;
        }
    }
    LL power(LL a, int b, int mod) {
        a %= mod;
        LL res = 1;
        for (; b; b /= 2, a = a * a % mod) {
            if (b % 2) {
                res = res * a % mod;
            }
        }
        return res;
    }
    LL powersum(int l, int r, int x, int mod) {
        auto itr = split(r + 1), itl = split(l);
        LL ans = 0;
        for (auto it = itl; it != itr; it++) {
            ans = (ans + power(it->v, x, mod) * (it->r - it->l + 1) % mod) % mod;
        }
        return ans;
    }
};



ST表

用于解决区间可重复贡献问题,需要满足 \(x \text{ 运算符 } x=x\) (如区间最大值:\(\max(x,x)=x\) 、区间 \(\gcd\)\(\gcd(x,x)=x\) 等),但是不支持修改操作。\(\mathcal O(NlogN)\) 预处理,\(\mathcal O(1)\) 查询。

备注:手写 log 函数比直接使用库函数 log2 更快,测试 例题

struct ST {
    int n;
    vector<int> in1, in2, lg;
    vector<vector<int>> Max, Min;
    ST(int n) : n(n), in1(n + 1), in2(n + 1), lg(n + 1) {
        for (int i = 2; i <= n; i++) {
            lg[i] = lg[i / 2] + 1;
        }
        Max.resize(n + 1, vector<int>(lg[n] + 1));
        Min.resize(n + 1, vector<int>(lg[n] + 1));
    }
    void init() {
        for (int i = 1; i <= n; i++) {
            Max[i][0] = in1[i];
            Min[i][0] = in2[i];
        }
        int k = lg[n];
        for (int j = 1; j <= k; j++) {
            for (int i = 1; i + (1 << j) - 1 <= n; i++) {
                Max[i][j] = max(Max[i][j - 1], Max[i + (1 << (j - 1))][j - 1]);
                Min[i][j] = min(Min[i][j - 1], Min[i + (1 << (j - 1))][j - 1]);
            }
        }
    }
    int getMax(int l, int r) {
        if (l > r) {
            swap(l, r);
        }
        int k = lg[r - l + 1];
        return max(Max[l][k], Max[r - (1 << k) + 1][k]);
    }
    int getMin(int l, int r) {
        if (l > r) {
            swap(l, r);
        }
        int k = lg[r - l + 1];
        return min(Min[l][k], Min[r - (1 << k) + 1][k]);
    }
};



大整数类(高精度计算)

集成了常见的高精度四则运算(包括高精度乘除高精度)与函数,可以与 #define int long long 同时存在。

const int base = 1000000000;
const int base_digits = 9; // 分解为九个数位一个数字
struct bigint {
    vector<int> a;
    int sign;

    bigint() : sign(1) {}
    bigint operator-() const {
        bigint res = *this;
        res.sign = -sign;
        return res;
    }
    bigint(long long v) {
        *this = v;
    }
    bigint(const string &s) {
        read(s);
    }
    void operator=(const bigint &v) {
        sign = v.sign;
        a = v.a;
    }
    void operator=(long long v) {
        a.clear();
        sign = 1;
        if (v < 0) sign = -1, v = -v;
        for (; v > 0; v = v / base) {
            a.push_back(v % base);
        }
    }

    // 基础加减乘除
    bigint operator+(const bigint &v) const {
        if (sign == v.sign) {
            bigint res = v;
            for (int i = 0, carry = 0; i < (int)max(a.size(), v.a.size()) || carry; ++i) {
                if (i == (int)res.a.size()) {
                    res.a.push_back(0);
                }
                res.a[i] += carry + (i < (int)a.size() ? a[i] : 0);
                carry = res.a[i] >= base;
                if (carry) {
                    res.a[i] -= base;
                }
            }
            return res;
        }
        return *this - (-v);
    }
    bigint operator-(const bigint &v) const {
        if (sign == v.sign) {
            if (abs() >= v.abs()) {
                bigint res = *this;
                for (int i = 0, carry = 0; i < (int)v.a.size() || carry; ++i) {
                    res.a[i] -= carry + (i < (int)v.a.size() ? v.a[i] : 0);
                    carry = res.a[i] < 0;
                    if (carry) {
                        res.a[i] += base;
                    }
                }
                res.trim();
                return res;
            }
            return -(v - *this);
        }
        return *this + (-v);
    }
    void operator*=(int v) {
        check(v);
        for (int i = 0, carry = 0; i < (int)a.size() || carry; ++i) {
            if (i == (int)a.size()) {
                a.push_back(0);
            }
            long long cur = a[i] * (long long)v + carry;
            carry = (int)(cur / base);
            a[i] = (int)(cur % base);
        }
        trim();
    }
    void operator/=(int v) {
        check(v);
        for (int i = (int)a.size() - 1, rem = 0; i >= 0; --i) {
            long long cur = a[i] + rem * (long long)base;
            a[i] = (int)(cur / v);
            rem = (int)(cur % v);
        }
        trim();
    }
    int operator%(int v) const {
        if (v < 0) {
            v = -v;
        }
        int m = 0;
        for (int i = a.size() - 1; i >= 0; --i) {
            m = (a[i] + m * (long long)base) % v;
        }
        return m * sign;
    }

    void operator+=(const bigint &v) {
        *this = *this + v;
    }
    void operator-=(const bigint &v) {
        *this = *this - v;
    }
    bigint operator*(int v) const {
        bigint res = *this;
        res *= v;
        return res;
    }
    bigint operator/(int v) const {
        bigint res = *this;
        res /= v;
        return res;
    }
    void operator%=(const int &v) {
        *this = *this % v;
    }

    bool operator<(const bigint &v) const {
        if (sign != v.sign) return sign < v.sign;
        if (a.size() != v.a.size()) return a.size() * sign < v.a.size() * v.sign;
        for (int i = a.size() - 1; i >= 0; i--)
            if (a[i] != v.a[i]) return a[i] * sign < v.a[i] * sign;
        return false;
    }
    bool operator>(const bigint &v) const {
        return v < *this;
    }
    bool operator<=(const bigint &v) const {
        return !(v < *this);
    }
    bool operator>=(const bigint &v) const {
        return !(*this < v);
    }
    bool operator==(const bigint &v) const {
        return !(*this < v) && !(v < *this);
    }
    bool operator!=(const bigint &v) const {
        return *this < v || v < *this;
    }

    bigint abs() const {
        bigint res = *this;
        res.sign *= res.sign;
        return res;
    }
    void check(int v) { // 检查输入的是否为负数
        if (v < 0) {
            sign = -sign;
            v = -v;
        }
    }
    void trim() { // 去除前导零
        while (!a.empty() && !a.back()) a.pop_back();
        if (a.empty()) sign = 1;
    }
    bool isZero() const { // 判断是否等于零
        return a.empty() || (a.size() == 1 && !a[0]);
    }
    friend bigint gcd(const bigint &a, const bigint &b) {
        return b.isZero() ? a : gcd(b, a % b);
    }
    friend bigint lcm(const bigint &a, const bigint &b) {
        return a / gcd(a, b) * b;
    }
    void read(const string &s) {
        sign = 1;
        a.clear();
        int pos = 0;
        while (pos < (int)s.size() && (s[pos] == '-' || s[pos] == '+')) {
            if (s[pos] == '-') sign = -sign;
            ++pos;
        }
        for (int i = s.size() - 1; i >= pos; i -= base_digits) {
            int x = 0;
            for (int j = max(pos, i - base_digits + 1); j <= i; j++) x = x * 10 + s[j] - '0';
            a.push_back(x);
        }
        trim();
    }
    friend istream &operator>>(istream &stream, bigint &v) {
        string s;
        stream >> s;
        v.read(s);
        return stream;
    }
    friend ostream &operator<<(ostream &stream, const bigint &v) {
        if (v.sign == -1) stream << '-';
        stream << (v.a.empty() ? 0 : v.a.back());
        for (int i = (int)v.a.size() - 2; i >= 0; --i)
            stream << setw(base_digits) << setfill('0') << v.a[i];
        return stream;
    }

    /* 大整数乘除大整数部分 */
    typedef vector<long long> vll;
    bigint operator*(const bigint &v) const { // 大整数乘大整数
        vector<int> a6 = convert_base(this->a, base_digits, 6);
        vector<int> b6 = convert_base(v.a, base_digits, 6);
        vll a(a6.begin(), a6.end());
        vll b(b6.begin(), b6.end());
        while (a.size() < b.size()) a.push_back(0);
        while (b.size() < a.size()) b.push_back(0);
        while (a.size() & (a.size() - 1)) a.push_back(0), b.push_back(0);
        vll c = karatsubaMultiply(a, b);
        bigint res;
        res.sign = sign * v.sign;
        for (int i = 0, carry = 0; i < (int)c.size(); i++) {
            long long cur = c[i] + carry;
            res.a.push_back((int)(cur % 1000000));
            carry = (int)(cur / 1000000);
        }
        res.a = convert_base(res.a, 6, base_digits);
        res.trim();
        return res;
    }
    friend pair<bigint, bigint> divmod(const bigint &a1,
                                       const bigint &b1) { // 大整数除大整数,同时返回答案与余数
        int norm = base / (b1.a.back() + 1);
        bigint a = a1.abs() * norm;
        bigint b = b1.abs() * norm;
        bigint q, r;
        q.a.resize(a.a.size());
        for (int i = a.a.size() - 1; i >= 0; i--) {
            r *= base;
            r += a.a[i];
            int s1 = r.a.size() <= b.a.size() ? 0 : r.a[b.a.size()];
            int s2 = r.a.size() <= b.a.size() - 1 ? 0 : r.a[b.a.size() - 1];
            int d = ((long long)base * s1 + s2) / b.a.back();
            r -= b * d;
            while (r < 0) r += b, --d;
            q.a[i] = d;
        }
        q.sign = a1.sign * b1.sign;
        r.sign = a1.sign;
        q.trim();
        r.trim();
        return make_pair(q, r / norm);
    }
    static vector<int> convert_base(const vector<int> &a, int old_digits, int new_digits) {
        vector<long long> p(max(old_digits, new_digits) + 1);
        p[0] = 1;
        for (int i = 1; i < (int)p.size(); i++) p[i] = p[i - 1] * 10;
        vector<int> res;
        long long cur = 0;
        int cur_digits = 0;
        for (int i = 0; i < (int)a.size(); i++) {
            cur += a[i] * p[cur_digits];
            cur_digits += old_digits;
            while (cur_digits >= new_digits) {
                res.push_back((int)(cur % p[new_digits]));
                cur /= p[new_digits];
                cur_digits -= new_digits;
            }
        }
        res.push_back((int)cur);
        while (!res.empty() && !res.back()) res.pop_back();
        return res;
    }
    static vll karatsubaMultiply(const vll &a, const vll &b) {
        int n = a.size();
        vll res(n + n);
        if (n <= 32) {
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    res[i + j] += a[i] * b[j];
                }
            }
            return res;
        }

        int k = n >> 1;
        vll a1(a.begin(), a.begin() + k);
        vll a2(a.begin() + k, a.end());
        vll b1(b.begin(), b.begin() + k);
        vll b2(b.begin() + k, b.end());

        vll a1b1 = karatsubaMultiply(a1, b1);
        vll a2b2 = karatsubaMultiply(a2, b2);

        for (int i = 0; i < k; i++) a2[i] += a1[i];
        for (int i = 0; i < k; i++) b2[i] += b1[i];

        vll r = karatsubaMultiply(a2, b2);
        for (int i = 0; i < (int)a1b1.size(); i++) r[i] -= a1b1[i];
        for (int i = 0; i < (int)a2b2.size(); i++) r[i] -= a2b2[i];

        for (int i = 0; i < (int)r.size(); i++) res[i + k] += r[i];
        for (int i = 0; i < (int)a1b1.size(); i++) res[i] += a1b1[i];
        for (int i = 0; i < (int)a2b2.size(); i++) res[i + n] += a2b2[i];
        return res;
    }

    void operator*=(const bigint &v) {
        *this = *this * v;
    }
    bigint operator/(const bigint &v) const {
        return divmod(*this, v).first;
    }
    void operator/=(const bigint &v) {
        *this = *this / v;
    }
    bigint operator%(const bigint &v) const {
        return divmod(*this, v).second;
    }
    void operator%=(const bigint &v) {
        *this = *this % v;
    }
};



KD Tree

这部分的代码来自我的队友 \(\pmb{Hamine}\)

struct KDT {
    constexpr static int N = 1e5 + 10, K = 2;
    double alpha = 0.725;
    struct node {
        int info[K];
        int mn[K], mx[K];
    } tr[N];
    int ls[N], rs[N], siz[N], id[N], d[N];
    int idx, rt, cur;
    int ans;
    KDT() {
        rt = 0;
        cur = 0;
        memset(ls, 0, sizeof ls);
        memset(rs, 0, sizeof rs);
        memset(d, 0, sizeof d);
    }
    void apply(int p, int son) {
        if (son) {
            for (int i = 0; i < K; i++) {
                tr[p].mn[i] = min(tr[p].mn[i], tr[son].mn[i]);
                tr[p].mx[i] = max(tr[p].mx[i], tr[son].mx[i]);
            }
            siz[p] += siz[son];
        }
    }
    void maintain(int p) {
        for (int i = 0; i < K; i++) {
            tr[p].mn[i] = tr[p].info[i];
            tr[p].mx[i] = tr[p].info[i];
        }
        siz[p] = 1;
        apply(p, ls[p]);
        apply(p, rs[p]);
    }
    int build(int l, int r) {
        if (l > r) return 0;
        vector<double> avg(K);
        for (int i = 0; i < K; i++) {
            for (int j = l; j <= r; j++) {
                avg[i] += tr[id[j]].info[i];
            }
            avg[i] /= (r - l + 1);
        }
        vector<double> var(K);
        for (int i = 0; i < K; i++) {
            for (int j = l; j <= r; j++) {
                var[i] += (tr[id[j]].info[i] - avg[i]) * (tr[id[j]].info[i] - avg[i]);
            }
        }
        int mid = (l + r) / 2;
        int x = max_element(var.begin(), var.end()) - var.begin();
        nth_element(id + l, id + mid, id + r + 1, [&](int a, int b) {
            return tr[a].info[x] < tr[b].info[x];
        });
        d[id[mid]] = x;
        ls[id[mid]] = build(l, mid - 1);
        rs[id[mid]] = build(mid + 1, r);
        maintain(id[mid]);
        return id[mid];
    }
    void print(int p) {
        if (!p) return;
        print(ls[p]);
        id[++idx] = p;
        print(rs[p]);
    }
    void rebuild(int &p) {
        idx = 0;
        print(p);
        p = build(1, idx);
    }
    bool bad(int p) {
        return alpha * siz[p] <= max(siz[ls[p]], siz[rs[p]]);
    }
    void insert(int &p, int cur) {
        if (!p) {
            p = cur;
            maintain(p);
            return;
        }
        if (tr[p].info[d[p]] > tr[cur].info[d[p]]) insert(ls[p], cur);
        else insert(rs[p], cur);
        maintain(p);
        if (bad(p)) rebuild(p);
    }
    void insert(vector<int> &a) {
        cur++;
        for (int i = 0; i < K; i++) {
            tr[cur].info[i] = a[i];
        }
        insert(rt, cur);
    }
    bool out(int p, vector<int> &a) {
        for (int i = 0; i < K; i++) {
            if (a[i] < tr[p].mn[i]) {
                return true;
            }
        }
        return false;
    }
    bool in(int p, vector<int> &a) {
        for (int i = 0; i < K; i++) {
            if (a[i] < tr[p].info[i]) {
                return false;
            }
        }
        return true;
    }
    bool all(int p, vector<int> &a) {
        for (int i = 0; i < K; i++) {
            if (a[i] < tr[p].mx[i]) {
                return false;
            }
        }
        return true;
    }
    void query(int p, vector<int> &a) {
        if (!p) return;
        if (out(p, a)) return;
        if (all(p, a)) {
            ans += siz[p];
            return;
        }
        if (in(p, a)) ans++;
        query(ls[p], a);
        query(rs[p], a);
    }
    int query(vector<int> &a) {
        ans = 0;
        query(rt, a);
        return ans;
    }
};
posted @ 2022-09-10 12:25  hh2048  阅读(600)  评论(0编辑  收藏  举报