转载请注明出处

杂项

卡时

有一个 clock() 函数,返回程序运行时间。

while ((double)clock()/CLOCKS_PER_SEC < MAX_TIME) action();

这样子就会一直跑action,直到用时即将超过时间限制

MAX_TIME 是一个自定义的略小于时限的数(秒)

O1快速乘

inline LL qmul(LL x, LL y, LL p) {
    LL z = (LD) x / p * y;
    LL res = (ULL) x * y - (ull) z * p;
    return (res + p) % p;
}

随机数生成器

static mt19937 gen(time(nullptr));
//static mt19937 gen(chrono::steady_clock::now().time_since_epoch().count()); //更高精度的时间戳增加随机性
inline int randInt(int l, int r) {                  // 支持负数, 范围[l, r]
    uniform_int_distribution<int> d(l, r);          // 确保分布均匀
    return d(gen);
}

inline double randDouble(double l, double r) {     // 支持负数, 范围[l, r)
    uniform_real_distribution<double> d(l, r);     // 确保分布均匀
    return d(gen);
}

any_hash (用于 unordered_map, unordered_set等防止哈希冲突,以及使其支持PII)

struct any_hash {
    static size_t get_seed() {
        static const size_t sd = []() {
            mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
            return static_cast<size_t>(rng());
        }();
        return sd;
    }

    template<typename T>
    size_t operator()(const T &x) const {
        return hash<T>{}(x) ^ (get_seed() + 0x9e3779b9 + (x << 6) + (x >> 2));
    }

    template<typename T1, typename T2>
    size_t operator()(const pair<T1, T2> &p) const {
        size_t hash1 = (*this)(p.first);
        size_t hash2 = (*this)(p.second);
        return hash1 ^ (hash2 + get_seed() + 0x9e3779b9 + (hash1 << 6) + (hash1 >> 2));
    }
};

离散化

insert 之后请务必先 buildget

struct Discretizer {
    vector<int> a;

    inline void insert(int x) { a.push_back(x); }

    void build() {
        sort(a.begin(), a.end());
        a.resize(unique(a.begin(), a.end()) - a.begin());
    }

    inline int get(int x) { return lower_bound(a.begin(), a.end(), x) - a.begin() + 1; }
};

数据结构

莫队

基础莫队(极限数据量 $5 \times 10^5$/s)

vector<int> pos(50005), ans(50005), cnt(50005), a(50005);

struct XX {
    int L, R, p;
} q[200005];

bool cmp(XX A, XX B) {
    if (pos[A.L] != pos[B.L])return pos[A.L] < pos[B.L];
    if (pos[A.L] & 1) return A.R > B.R;
    else return A.R < B.R;
}
int A = 0;
inline void add(int x) {
    A -= cnt[a[x]] * cnt[a[x]];
    ++cnt[a[x]];
    A += cnt[a[x]] * cnt[a[x]];
}

inline void del(int x) {
    A -= cnt[a[x]] * cnt[a[x]];
    --cnt[a[x]];
    A += cnt[a[x]] * cnt[a[x]];
}

void solve() {
    int n, m, k;
    cin >> n >> m >> k;
    int block = sqrt(n);  //块长
    for (int i = 0; i < n; ++i) {
        cin >> a[i];
        pos[i] = i / (block + 1);    //索引所属块
    }
    for (int i = 0; i < m; ++i) {
        cin >> q[i].L >> q[i].R;
        q[i].p = i;
        --q[i].L, --q[i].R;
    }
    sort(q, q + m, cmp);
    int zl = 0, zr = -1;
    for (int i = 0; i < m; ++i) {    //该处放"暴力求解"代码
        while (zl < q[i].L) del(zl++);
        while (zr > q[i].R) del(zr--);
        while (zl > q[i].L) add(--zl);
        while (zr < q[i].R) add(++zr);
        ans[q[i].p] = A;
    }
    for (int i = 0; i < m; ++i) {
        cout << ans[i] << endl;
    }
}

莫队+分块求区间MEX(离线)

const int N = 2e5 + 10, NN = 500;

vector<int> cnt(200005), a(200005), pos(200005), ans(200005), b(502);
struct XX {
    int L, R, id;
} q[200005];

bool cmp(XX A, XX B) {
    if (pos[A.L] != pos[B.L]) return A.L < B.L;
    else {
        if (pos[A.L] & 1) return A.R > B.R;
        else return A.R < B.R;
    }
}

inline void add(int p) {
    if (++cnt[a[p]] == 1) ++b[a[p] / NN];
}

inline void del(int p) {
    if (--cnt[a[p]] == 0) --b[a[p] / NN];
}

inline int check() {
    for (int i = 0; i < 501; ++i) {
        if (b[i] != NN) {
            for (int j = NN * i; j < 200005; ++j) {
                if (!cnt[j]) return j;
            }
        }
    }
}

void solve() {
    int n, m;
    cin >> n >> m;
    int block = static_cast<int>(sqrt(n));
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
        pos[i + 1] = i / (block + 1);
    }

    for (int i = 0; i < m; ++i) {
        cin >> q[i].L >> q[i].R;
        q[i].id = i;
    }
    sort(q, q + m, cmp);
    int L = 1, R = 0;
    for (int i = 0; i < n; ++i) {
        while(L < q[i].L) del(L++);
        while(L > q[i].L) add(--L);
        while(R < q[i].R) add(++R);
        while(R > q[i].R) del(R--);
        ans[q[i].id] = check();
    }
    for (int i = 0; i < m; ++i) {
        cout << ans[i] << endl;
    }
}

带修莫队

vector<int> pos(150000), a(150000), ans;

struct XX {
    int L, R, T, p;
} q[150000];

struct FIX {
    int x, v;  //位置,值
};

bool cmp(XX A, XX B) {
    if (pos[A.L] != pos[B.L])return pos[A.L] < pos[B.L];
    else if (pos[A.R] != pos[B.R]) {
        if (pos[A.L] & 1) return A.R > B.R;
        else return A.R < B.R;
    }
    else return A.T < B.T;
}

int A = 0;
vector<int> cnt(1000001);

inline void add(int x) {
    if (++cnt[x] == 1) ++A;
}

inline void del(int x) {
    if (--cnt[x] == 0) --A;
}

vector<FIX> r(133335); //下标为时间戳,什么时候修改了什么值

void solve() {
    //ans.reserve(150000); 预分配空间,防止push_back() 产生内存移动
    int n, m, T = 0;
    cin >> n >> m;
    int block = pow(n,  2.0 / 3.0);
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
        pos[i] = i / block;
    }

    for (int i = 0; i < m; ++i) {
        char op;
        cin >> op;
        if (op == 'Q') {
            cin >> q[i].L >> q[i].R;
            q[i].T = T;
            q[i].p = static_cast<int>(ans.size());
            ans.push_back(0);
        } else {
            ++T;
            cin >> r[T].x >> r[T].v;
        }
    }
    sort(q, q + m, cmp);
    int zl = 1, zr = 0;
    T = 0;
    for (int i = 0; i < m; ++i) {
        while (zl < q[i].L) del(a[zl++]);
        while (zr > q[i].R) del(a[zr--]);
        while (zl > q[i].L) add(a[--zl]);
        while (zr < q[i].R) add(a[++zr]);
        while (T < q[i].T) {
            ++T;
            if (zl <= r[T].x && r[T].x <= zr) {
                add(r[T].v);
                del(a[r[T].x]);
            }
            swap(a[r[T].x], r[T].v);
        }
        while (T > q[i].T) {
            if (zl <= r[T].x && r[T].x <= zr) {
                add(r[T].v);
                del(a[r[T].x]);
            }
            swap(a[r[T].x], r[T].v);
            --T;
        }
        ans[q[i].p] = A;
    }
    for (long long i: ans) cout << i << endl;
}

Trie

普通Trie

template <int MAXN, int ALPHA_SIZE, char BASE_CHAR>
struct Trie {
    int tr[MAXN][ALPHA_SIZE];
    int cnt[MAXN];
    int pass_cnt[MAXN]; //如不需要删除或者start_with可以不开该数组节省空间
    int node_cnt;

    void init() {
        memset(tr[0], 0, ALPHA_SIZE * sizeof(int));
        memset(cnt, 0, sizeof(cnt));
        memset(pass_cnt, 0, sizeof(pass_cnt));
        node_cnt = 1;
    }

    void insert(const string &s) {
        int p = 0;
        for (int i = 0; s[i]; ++i) {
            int c = s[i] - BASE_CHAR;
            if (!tr[p][c]) {
                memset(tr[node_cnt], 0, ALPHA_SIZE * sizeof(int));
                tr[p][c] = node_cnt++;
            }
            p = tr[p][c];
            pass_cnt[p]++;
        }
        cnt[p]++;
    }

    void erase(const string &s) {
        int p = 0;
        for (int i = 0; s[i]; ++i) {
            p = tr[p][s[i] - BASE_CHAR];
            if (!p) return;
            pass_cnt[p]--;
        }
        if (cnt[p] > 0) cnt[p]--;
    }

    int search(const string &s) {
        int p = 0;
        for (int i = 0; s[i]; ++i) {
            p = tr[p][s[i] - BASE_CHAR];
            if (!p) break;
        }
        return p ? cnt[p] : 0;
    }

    bool starts_with(const string &s) {
        int p = 0;
        for (int i = 0; s[i]; ++i) {
            p = tr[p][s[i] - BASE_CHAR];
            if (!p || pass_cnt[p] == 0) return false;
        }
        return true;
    }

};

/* 注意:内开需要把静态改为vector

初始化<节点容量, 字符数, 起始字符>

使用示例:

// 小写字母 trie(1e5 节点容量)
Trie<100000, 26, 'a'> lower_trie;

// 数字 trie(1e4 节点容量)
Trie<10000, 10, 0> num_trie;

// 01 trie (1e4节点容量)
Trie<10000, 2, 0> num_trie;

// 大写字母 trie(带自定义容量)
Trie<50000, 26, 'A'> upper_trie;

// 初始化
lower_trie.init(); //一定要记得
lower_trie.insert("algorithm");
lower_trie.search("algo");  // 返回 0
lower_trie.search("algorithm");  // 返回 1
lower_trie.starts_with("algo"); // 返回 true
*/

01 Trie 求最大异或值

template <int MAX_BIT = 31, typename T = int>  // 默认处理 31位int整数(符号位除外)

struct BTrie {
    static const int SIZE = MAX_BIT * 5e5 + 10;  // 根据问题规模调整
    int tr[SIZE][2];
    int cnt[SIZE];  // 经过该节点的数字数量
    int node_cnt;

    inline void init() {
        node_cnt = cnt[0] = tr[0][0] = tr[0][1] = 0;
    }

    // 插入数字(二进制从高位到低位存储)
    void insert(T num) {
        int p = 0;
        for (int i = MAX_BIT-1; i >= 0; --i) {
            int b = (num >> i) & 1;
            if (!tr[p][b]) {
                tr[p][b] = ++node_cnt;
                cnt[node_cnt] = tr[node_cnt][0] = tr[node_cnt][1] = 0;
            }
            p = tr[p][b];
            cnt[p]++;
        }
    }

    // 删除数字(需保证数字存在)
    void erase(T num) {
        int p = 0;
        for (int i = MAX_BIT-1; i >= 0; --i) {
            int b = (num >> i) & 1;
            p = tr[p][b];
            cnt[p]--;
        }
    }
    // 查询与 num 异或后的最大值
    T max_xor(T num) {
        if (!node_cnt) return 0;  // 空 Trie
        T res = 0;
        int p = 0;
        for (int i = MAX_BIT-1; i >= 0; --i) {
            int best = ((num >> i) & 1) ^ 1;  // 优先选相反的位

            if (tr[p][best] && cnt[tr[p][best]] > 0) {
                res |= ((T)1 << i);
                p = tr[p][best];
            } else {
                p = tr[p][best ^ 1];
            }
        }
        return res;
    }
};


/* 使用示例:
BTrie<31> trie;  // 处理 31 位非负整数
BTrie <32, unsigned int> trie;  // 处理 32位
*/

//可用于处理 (最大/最小 AND/OR/XOR) 区间最大XOR和(配合前缀XOR数组)

可持久化Trie

template <int MAX_BIT = 31, typename T = int>
struct PersistentBTrie {
    struct Node {
        int ch[2] = {0, 0};
        int cnt = 0;
    };
    std::vector<Node> nodes;
    std::vector<int> roots;  // 各版本根节点索引

    PersistentBTrie() {
        nodes.emplace_back();  // 初始化空节点(版本0)
        roots.push_back(0);    // 初始版本0
    }

    // 插入新数字并生成新版本,prev_version为基准版本号
    int insert(T num, int prev_version) {
        int new_root = nodes.size();
        nodes.push_back(nodes[roots[prev_version]]); // 克隆旧根节点
        nodes[new_root].cnt++; // 当前路径计数+1
        roots.push_back(new_root); // 记录新版本根节点

        int curr = new_root;
        for (int i = MAX_BIT-1; i >= 0; --i) {
            int b = (num >> i) & 1;
            int next = nodes[curr].ch[b];
            int new_node = nodes.size();
            nodes.push_back(nodes[next]); // 克隆子节点
            nodes[new_node].cnt++;        // 更新当前路径计数
            nodes[curr].ch[b] = new_node; // 链接新节点
            curr = new_node;
        }
        return roots.size() - 1; // 返回新版本号
    }

    // 查询区间 [l_ver, r_ver] 内与num异或的最大值
    T max_xor(int l_ver, int r_ver, T num) {
        int l_root = roots[l_ver], r_root = roots[r_ver];
        T res = 0;
        for (int i = MAX_BIT-1; i >= 0; --i) {
            int b = (num >> i) & 1;
            int target = b ^ 1; // 期望的bit

            int r_ch = nodes[r_root].ch[target];
            int l_ch = nodes[l_root].ch[target];
            int diff = nodes[r_ch].cnt - nodes[l_ch].cnt;

            if (diff > 0) { // 存在该路径
                res |= T(1) << i;
                r_root = r_ch;
                l_root = l_ch;
            } else { // 路径不存在,选择另一边
                r_root = nodes[r_root].ch[target ^ 1];
                l_root = nodes[l_root].ch[target ^ 1];
            }
        }
        return res;
    }
};

树状数组

普通树状数组(单点修改, 区间查询)

template<typename T>
struct Fenwick {
    vector<T> tree;
    int n;

    static inline int lowbit(int x) { return x & (-x); }

    explicit Fenwick(int size) : n(size), tree(size + 1, 0) {}

    void add(int x, T v) {
        while (x <= n) {
            tree[x] += v;
            x += lowbit(x);
        }
    }

    T query(int x) const {
        T res = 0;
        while (x) {
            res += tree[x];
            x -= lowbit(x);
        }
        return res;
    }

    // 查询区间和 [l, r]
    T query(int l, int r) { return query(r) - query(l - 1); }
    T operator[](int x) const { return query(x) - query(x - 1); }
};

普通树状数组区间修改

template<typename T>
struct FenwickRange {
    vector<T> tree1, tree2;  // 两个差分树
    int n;

    static inline int lowbit(int x) { return x & (-x); }

    explicit FenwickRange(int size) : n(size), tree1(size + 2, 0), tree2(size + 2, 0) {}

    void _add(vector<T>& tree, int x, T v) {    // 同时更新两个树
        while (x <= n) {
            tree[x] += v;
            x += lowbit(x);
        }
    }

    void add(int l, int r, T v) {   // 区间 [l, r] 增加 v
        _add(tree1, l, v);
        _add(tree1, r + 1, -v);
        _add(tree2, l, v * l);
        _add(tree2, r + 1, -v * (r + 1));
    }

    T query(int x) {    // 查询前缀和 [1, x]
        T res1 = 0, res2 = 0;
        int t = x;
        while (x > 0) {
            res1 += tree1[x];
            res2 += tree2[x];
            x -= lowbit(x);
        }
        return (t + 1) * res1 - res2;
    }

    // 查询区间和 [l, r]
    T query(int l, int r) { return query(r) - query(l - 1); }

    T operator[](int x) const { return query(x, x); }
};

二维树状数组

template<typename T>
struct Fenwick2D {
    vector<vector<T>> tree;
    int rows, cols;

    static int lowbit(int x) { return x & (-x); }

    explicit Fenwick2D(int r, int c) : rows(r), cols(c), tree(r + 1, vector<T>(c + 1, 0)) {}

    void add(int x, int y, T v) {  // 在位置 (x,y) 处增加值 v(1-based)
        for (int i = x; i <= rows; i += lowbit(i)) {
            for (int j = y; j <= cols; j += lowbit(j)) {
                tree[i][j] += v;
            }
        }
    }

    T query(int x, int y) const {   // 查询(1,1) 到 (x,y) 的矩形和
        T res = 0;
        for (int i = x; i > 0; i -= lowbit(i)) {
            for (int j = y; j > 0; j -= lowbit(j)) {
                res += tree[i][j];
            }
        }
        return res;
    }

    T query(int x1, int y1, int x2, int y2) const {    // 矩形区域 [x1,y1] 到 [x2,y2] 的和(闭区间)
        return query(x2, y2) - query(x1 - 1, y2) - query(x2, y1 - 1) + query(x1 - 1, y1 - 1);
    }
};

二维树状数组区间修改

template<typename T>
struct Fenwick2DRange {
    struct {
        vector<vector<T>> tree;
        int rows{}, cols{};

        void init(int r, int c) {
            rows = r;
            cols = c;
            tree.assign(r + 2, vector<T>(c + 2, 0));
        }

        void add(int x, int y, T v) {
            for (int i = x; i <= rows; i += lowbit(i))
                for (int j = y; j <= cols; j += lowbit(j))
                    tree[i][j] += v;
        }

        T query(int x, int y) {
            T res = 0;
            for (int i = x; i > 0; i -= lowbit(i))
                for (int j = y; j > 0; j -= lowbit(j))
                    res += tree[i][j];
            return res;
        }
    } D, Dx, Dy, Dxy;

    int rows, cols;

    static int lowbit(int x) { return x & (-x); }

    explicit Fenwick2DRange(int r, int c) : rows(r), cols(c) {
        D.init(r, c);
        Dx.init(r, c);
        Dy.init(r, c);
        Dxy.init(r, c);
    }

    void range_add(int x1, int y1, int x2, int y2, T v) {
        D.add(x1, y1, v);
        D.add(x1, y2+1, -v);
        D.add(x2+1, y1, -v);
        D.add(x2+1, y2+1, v);

        Dx.add(x1, y1, v * (x1-1));
        Dx.add(x1, y2+1, -v * (x1-1));
        Dx.add(x2+1, y1, -v * x2);
        Dx.add(x2+1, y2+1, v * x2);

        Dy.add(x1, y1, v * (y1-1));
        Dy.add(x1, y2+1, -v * y2);
        Dy.add(x2+1, y1, -v * (y1-1));
        Dy.add(x2+1, y2+1, v * y2);

        Dxy.add(x1, y1, v * (x1-1) * (y1-1));
        Dxy.add(x1, y2+1, -v * (x1-1) * y2);
        Dxy.add(x2+1, y1, -v * x2 * (y1-1));
        Dxy.add(x2+1, y2+1, v * x2 * y2);
    }

    T query(int x, int y) {
        return x * y * D.query(x, y)
            - y * Dx.query(x, y)
            - x * Dy.query(x, y)
            + Dxy.query(x, y);
    }

    T query(int x1, int y1, int x2, int y2) {
        return query(x2, y2)
            - query(x1-1, y2)
            - query(x2, y1-1)
            + query(x1-1, y1-1);
    }

    T get(int x, int y) { return query(x, y, x, y); }

    /*
    Fenwick2DRange<int> fr(100, 100);  // 创建 100x100 的二维区间树 (行, 列)

    // 在 [3,5] 到 [10,20] 的矩形区域加 5
    fr.range_add(3, 5, 10, 20, 5);

    // 查询 [1,1] 到 [50,50] 的和
    fr.query(1, 1, 50, 50);

    // 单点查询 (7,8)
    fr.get(7, 8);

    */
};

线段树

线段树区间加减乘

template<int MAX_N>
struct LazySegmentTree {
private:
    static constexpr int MAX_TREE_SIZE = 4 * MAX_N;
    ll tree[MAX_TREE_SIZE];
    struct LazyTag { ll mul, add; } lazy[MAX_TREE_SIZE];
    ll mod;
    int n;
    vector<ll> a;

    // 辅助函数:处理负数的取模
    inline ll mod_val(ll x) const {
        if (mod == 0) return x;
        return (x % mod + mod) % mod;
    }

    void push_down(int L, int M, int R, int p) {
        if ((lazy[p].mul != 1 || lazy[p].add != 0) && L != R) {
            int lc = p<<1, rc = p<<1|1;

            // 更新左子树
            tree[lc] = mod_val(tree[lc] * lazy[p].mul + lazy[p].add * (M-L+1));
            // 更新右子树
            tree[rc] = mod_val(tree[rc] * lazy[p].mul + lazy[p].add * (R-M));

            // 合并标记到左子树
            lazy[lc].add = mod_val(lazy[lc].add * lazy[p].mul + lazy[p].add);
            lazy[lc].mul = mod_val(lazy[lc].mul * lazy[p].mul);

            // 合并标记到右子树
            lazy[rc].add = mod_val(lazy[rc].add * lazy[p].mul + lazy[p].add);
            lazy[rc].mul = mod_val(lazy[rc].mul * lazy[p].mul);

            // 重置当前标记
            lazy[p].mul = 1;
            lazy[p].add = 0;
        }
    }

    void push_up(int p) {
        tree[p] = mod_val(tree[p<<1] + tree[p<<1|1]);
    }

    void build(int L, int R, int p) {
        lazy[p] = {1, 0};
        if (L == R) {
            tree[p] = mod_val(a[L]);
            return;
        }
        int M = (L+R) >> 1;
        build(L, M, p<<1);
        build(M+1, R, p<<1|1);
        push_up(p);
    }

    void _update_add(int L, int R, int p, int l, int r, ll v) {
        if (l <= L && R <= r) {
            tree[p] = mod_val(tree[p] + v * (R-L+1));
            lazy[p].add = mod_val(lazy[p].add + v);
            return;
        }
        int M = (L+R) >> 1;
        push_down(L, M, R, p);
        if (l <= M) _update_add(L, M, p<<1, l, r, v);
        if (r > M) _update_add(M+1, R, p<<1|1, l, r, v);
        push_up(p);
    }

    void _update_mul(int L, int R, int p, int l, int r, ll v) {
        if (l <= L && R <= r) {
            tree[p] = mod_val(tree[p] * v);
            lazy[p].add = mod_val(lazy[p].add * v);
            lazy[p].mul = mod_val(lazy[p].mul * v);
            return;
        }
        int M = (L+R) >> 1;
        push_down(L, M, R, p);
        if (l <= M) _update_mul(L, M, p<<1, l, r, v);
        if (r > M) _update_mul(M+1, R, p<<1|1, l, r, v);
        push_up(p);
    }

    ll _query(int L, int R, int p, int l, int r) {
        if (l <= L && R <= r) return tree[p];
        int M = (L+R) >> 1;
        push_down(L, M, R, p);
        ll res = 0;
        if (l <= M) res += _query(L, M, p<<1, l, r);
        if (r > M) res += _query(M+1, R, p<<1|1, l, r);
        return mod_val(res);
    }

public:
    // 初始化:传入原始数组(1-based)和模数(0表示不取模)
    LazySegmentTree(const vector<ll>& arr, ll m = 0) : mod(m), n(arr.size()-1), a(arr) {
        assert(arr.size() > 1 && "Array should be 1-based");
        build(1, n, 1);
    }

    // 区间加 [l, r] 闭区间
    void range_add(int l, int r, ll v) {
        _update_add(1, n, 1, l, r, v);
    }

    // 区间乘 [l, r] 闭区间
    void range_mul(int l, int r, ll v) {
        _update_mul(1, n, 1, l, r, v);
    }

    // 区间查询 [l, r] 闭区间
    ll range_query(int l, int r) {
        return _query(1, n, 1, l, r);
    }
};

// 使用示例:
// vector<ll> arr(n+1); // 1-based
// LazySegmentTree<100000> st(arr, mod);
// st.range_add(l, r, v);
// st.range_mul(l, r, v);
// ll sum = st.range_query(l, r);

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

主席树求区间MEX(在线)

const int N = 2e5 + 10;
struct node {
    int l, r, v;
} tr[N * 30];
int cnt = 0, root[N];

inline void pushup(int x) {
    tr[x].v = min(tr[tr[x].l].v, tr[tr[x].r].v);
}

int update(int x, int v, int L, int R, int P) {
    if (L == R) {
        tr[++cnt].v = v;
        return cnt;
    }
    int nex = ++cnt, mid = (L + R) >> 1;
    tr[nex] = tr[P];
    if (x <= mid)  tr[nex].l = update(x, v, L, mid, tr[P].l);
    else tr[nex].r = update(x, v, mid + 1, R, tr[P].r);
    pushup(nex);
    return nex;
}

int query(int l, int L, int R, int P) {
    if (L == R) return L;
    int mid = (L + R) >> 1;
    if (tr[tr[P].l].v < l) return query(l, L, mid, tr[P].l);
    return query(l, mid + 1, R, tr[P].r);
}


void solve() {
    int n, q, tmp;
    cin >> n >> q;
    for (int i = 1; i <= n; i++) {
        cin >> tmp;
        root[i] = update(tmp + 1, i, 1, 2e5+1, root[i - 1]);
    }

    while(q--) {
        int l, r;
        cin >> l >> r;
        cout << query(l, 1, 2e5+1, root[r]) - query(l, 1, 2e5+1, root[l - 1]) << endl;
    }

}

链式前向星

const int MAXN = 5e5 + 5;  // 最大节点数
const int MAXM = 1e6 + 5;  // 最大边数(无向图*2)
struct Edge {
    int to, nex;
} e[MAXM];
int head[MAXN], cnt_edge;

inline void clear_edge(int n) {
    for (int i = 0; i <= n; ++i) head[i] = -1;
    cnt_edge = 0;

}

inline void add_edge(int u, int v) {
    e[++cnt_edge].to = v;
    e[cnt_edge].nex = head[u];
    head[u] = cnt_edge;
}

倍增LCA

constexpr int MAXN = 5e5 + 10;
constexpr int MAX_LOG = 20; // 需满足2^MAX_LOG > 最大深度



struct {
    int to, nex;
} e[MAXN << 1];

int head[MAXN << 1], cnt = 0;

inline void add(int x, int y) {
    e[++cnt].to = y;
    e[cnt].nex = head[x];
    head[x] = cnt;
}

void init(int n) {
    for (int i = 0; i <= n; ++i) head[i] = e[i].nex = -1;
    cnt = 0;
}

int depth[MAXN]; // 节点深度
int fa[MAXN][MAX_LOG]; // 倍增父亲数组

void dfs_lca(int u, int father) {
    depth[u] = depth[father] + 1;
    fa[u][0] = father;
    for (int i = 1; i < MAX_LOG; ++i)
        fa[u][i] = fa[fa[u][i - 1]][i - 1];
    for (int i = head[u]; i != -1; i = e[i].nex) {
        int v = e[i].to;
        if (v != father) dfs_lca(v, u);
    }
}

/*
//非递归预处理
void dfs_lca(int root, int father) {
    stack<pair<int, int>> s;
    s.push({root, father});
    while (!s.empty()) {
        auto [u, f] = s.top();
        s.pop();
        depth[u] = depth[f] + 1;
        fa[u][0] = f;
        for (int i = 1; i < MAX_LOG; ++i)
            fa[u][i] = fa[fa[u][i-1]][i-1];
        for (int i = head[u]; i != -1; i = e[i].nex) {
            int v = e[i].to;
            if (v != f)
                s.push({v, u}); // 子节点入栈
        }
    }
}
*/

inline void init_lca(int root) { // 初始化LCA
    depth[0] = -1;
    dfs_lca(root, 0);
}

int get_lca(int x, int y) {
    if (depth[x] < depth[y]) swap(x, y);
    // 先跳到同一层
    for (int i = MAX_LOG - 1; i >= 0; --i)
        if (depth[x] - (1 << i) >= depth[y])
            x = fa[x][i];
    if (x == y) return x;
    // 同时往上跳
    for (int i = MAX_LOG - 1; i >= 0; --i)
        if (fa[x][i] != fa[y][i])
            x = fa[x][i], y = fa[y][i];
    return fa[x][0];
}

int get_dist(int x, int y) {
    int lca = get_lca(x, y);
    return depth[x] + depth[y] - 2 * depth[lca];
}

树链剖分

struct Edge {
    int to, nex;
} edge[200010];

int cnt = 0, head[200010], n;

void init() {
    for (int i = 0; i < 200010; ++i) {
        edge[i].to = edge[i].nex = head[i] = -1;
    }
}

void add(int x, int y) {
    edge[++cnt].to = y;
    edge[cnt].nex = head[x];
    head[x] = cnt;
}

int tree[400020], lazy[400020];

inline void pu(int p) {
    tree[p] = tree[p << 1] + tree[p << 1 | 1];
}

inline void pd(int L, int M, int R, int p) {
    if (lazy[p] && L != R) {
        if (lazy[p] == INT_MIN) {
            tree[p << 1] = tree[p << 1 | 1] = 0;
            lazy[p << 1] = lazy[p << 1 | 1] = INT_MIN;
            lazy[p] = 0;
            return;
        }
        tree[p << 1] = lazy[p] * (1 + M - L),
                tree[p << 1 | 1] = lazy[p] * (R - M);
        lazy[p << 1] = lazy[p], lazy[p << 1 | 1] = lazy[p];
        lazy[p] = 0;
    }
}

void update(int L, int R, int p, int l, int r) {
    if (l <= L && R <= r) {
        tree[p] = R - L + 1;
        lazy[p] = 1;
        return;
    }
    int M = L + ((R - L) >> 1);
    pd(L, M, R, p);
    if (l <= M) update(L, M, p << 1, l, r);
    if (r > M) update(M + 1, R, p << 1 | 1, l, r);
    pu(p);
}

void resett(int L, int R, int p, int l, int r) {
    if (l <= L && R <= r) {
        tree[p] = 0;
        lazy[p] = INT_MIN;
        return;
    }
    int M = L + ((R - L) >> 1);
    pd(L, M, R, p);
    if (l <= M) resett(L, M, p << 1, l, r);
    if (r > M) resett(M + 1, R, p << 1 | 1, l, r);
    pu(p);
}

int query(int L, int R, int p, int l, int r) {
    int M = L + ((R - L) >> 1), sum = 0;
    pd(L, M, R, p);
    if (L >= l && R <= r) {
        return tree[p];
    }

    if (l <= M) sum = query(L, M, p << 1, l, r);
    if (r > M) sum += query(M + 1, R, p << 1 | 1, l, r);
    return sum;
}

int son[100005], id[100005], fa[100005], deep[100005], siz[100005], top[100005];

void dfs1(int x, int father) {
    deep[x] = deep[father] + 1;
    fa[x] = father;
    siz[x] = 1;
    for (int i = head[x]; ~i; i = edge[i].nex) {
        int s = edge[i].to;
        if (s != father) {
            fa[s] = x;
            dfs1(s, x);
            siz[x] += siz[s];
            if (!son[x] || siz[son[x]] < siz[s]) son[x] = s;
        }
    }
}

int num = 0;

void dfs2(int x, int topx) {
    id[x] = ++num;
    top[x] = topx;
    if (!son[x]) return;
    dfs2(son[x], topx);
    for (int i = head[x]; ~i; i = edge[i].nex) {
        int y = edge[i].to;
        if (y != fa[x] && y != son[x]) dfs2(y, y);
    }
}

int query_range(int x, int y) { //查询树上路径 x 到 y 的和
    int ans = 0;
    while (top[x] != top[y]) {
        if (deep[top[x]] < deep[top[y]]) swap(x, y);
        ans += query(1, n, 1, id[top[x]], id[x]);
        x = fa[top[x]];
    }
    if (deep[x] > deep[y]) swap(x, y);
    ans += query(1, n, 1, id[x], id[y]);
    return ans;
}

void update_range(int x, int y) {  //将路径 x 到 y 的所有节点设为 1。
    while (top[x] != top[y]) {
        if (deep[top[x]] < deep[top[y]]) swap(x, y);
        update(1, n, 1, id[top[x]], id[x]);
        x = fa[top[x]];
    }
    if (deep[x] > deep[y]) swap(x, y);
    update(1, n, 1, id[x], id[y]);
}

void update_tree(int x) { //将子树 x 的所有节点设为 1。
    update(1, n, 1, id[x], id[x] + siz[x] - 1);
}

void reset_tree(int x) { //将子树 x 的所有节点设为 0。
    resett(1, n, 1, id[x], id[x] + siz[x] - 1);
}

int query_tree(int x) { //查询子树的和
    return query(1, n, 1, id[x], id[x] + siz[x] - 1);
}

并查集

int find(inx x) {
    return a[x] == x ? x : find(a[x]);
}

带权并查集

int find(int x) {
    if (a[x] != x) {
        int root = find(a[x]);
        w[x] += w[a[x]];
        a[x] = root;
    }
    return a[x];
}

void unite(int x, int y, int d) {
    int fx = find(x), fy = find(y);
    if (fx != fy) {
        a[fx] = fy;
        w[fx] = (d + w[y] - w[x];
    }
}
//x合并至y

字符串

KMP

void getNext(vector<int> &next, string s) {
    int m = s.size();
    next.clear();
    next.resize(m);
    for (int i = 1, j = 0; i < m; i++) {
        while (j > 0 && s[i] != s[j]) j = next[j - 1];
        if (s[i] == s[j]) j++;
        next[i] = j;
    }
}
//返回匹配的位置

vector<int> kmp(string &a, string &b) {
    vector<int> next, res;
    getNext(next, b);
    for (int i = 0, j = 0; i < a.size();) {
        if (j < b.size() && a[i] == b[j]) ++i, ++j;
        else while (a[i] != b[j]) {
                if (j == 0) {
                    ++i;
                    break;
                }
                j = next[j - 1];
            }
        if (j == b.size()) res.push_back(i - b.size() + 1);
    }
    //for (int i: res) cout << i << endl;
    //for (int i: next) cout << i << ' ';
    return res;
}

HASH

常用模数 $13331$$131$

hash二分求回文子串数量

#define hash_t unsigned __int128
#define hash_tt unsigned long long


hash_t pre[100005], suf[100005], p[100005], base = 13333333111;
hash_tt  pre1[100005], suf1[100005], p1[100005], base1 = 13331;
int a[100005], v[100005];

void init(int n) {
    p[0] = p1[0] = 1;
    for (int i = 1; i <= n; i++) {
        pre[i] = pre[i-1] * base + a[i];
        pre1[i] = pre1[i-1] * base1 + a[i];
        p[i] = p[i-1] * base;
        p1[i] = p1[i-1] * base1;
    }

    for (int i = n; i >= 1; i--) {
        suf[i] = suf[i+1] * base + a[i];
        suf1[i] = suf1[i+1] * base1 + a[i];
    }
}

inline bool check(int l, int r) {
    if (suf[l] - suf[r+1] * p[r-l+1] == pre[r] - pre[l-1] * p[r-l+1] &&
    suf1[l] - suf1[r+1] * p1[r-l+1] == pre1[r] - pre1[l-1] * p1[r-l+1])
        return true;
    return false;
}

int count_huiwen(int n) {
    int count = 0;

    for (int i = 1; i <= n; ++i) {
        int l = 0, r = min(i - 1, n - i), ans = -1;
        while (l <= r) {
            int mid = (l + r) >> 1;
            if (check(i - mid, i + mid)) ans = mid, l = mid + 1;
            else r = mid - 1;
        }
        count += ans + 1;
    }

    for (int i = 1; i <= n - 1; ++i) {
        int l = 1, r = min(i, n - i), ans = 0;
        while (l <= r) {
            int mid = (l + r) >> 1;
            if (i - mid + 1 < 1 || i + mid > n || i - mid + 1 > i + mid) {
                r = mid - 1;
                continue;
            }
            if (check(i - mid + 1, i + mid)) ans = mid, l = mid + 1;
            else r = mid - 1;
        }
        count += ans;
    }

    return count;
}

HASH求双回文子串长度(可求对应位置的前后回文长度)(代替马拉车)

参考题目,洛谷P4555,求字符串中双回文子串的最大长度。

#include <bits/stdc++.h>

#define PII pair<int,int>
#define LL long long
#define LD long double
#define ULL unsigned int
#define endl '\n'
using namespace std;

ULL base = 131;
vector<ULL> h1(100005), h2(100005), p(100005);
int mx_pre[100005], mx_suf[100005];

inline ULL pre_hash(int l, int r) {
    return h1[r] - h1[l - 1] * p[r - l + 1];
}

inline ULL suf_hash(int l, int r) {
    return h2[l] - h2[r+1] * p[r - l + 1];
}

void solve() {
    string str;
    int mx = 0;
    cin >> str;
    p[0] = mx_pre[1] = mx_suf[str.size()] = 1;;
    for (int i = 1; i < 100005; ++i) p[i] = p[i-1] * base;
    for (int i = 0; i < str.size(); ++i) h1[i+1] = h1[i] * base + static_cast<ULL>(str[i] - 'a' + 1);
    for (int i = str.size() - 1; i >= 0; --i) h2[i + 1] = h2[i + 2] * base + static_cast<ULL>(str[i] - 'a' + 1);

    for (int i = 2; i <= str.size(); ++i) {
        int tmp = min(i, mx_pre[i - 1] + 2);
        while(pre_hash(i - tmp + 1,i) != suf_hash(i - tmp + 1, i)) --tmp;
        mx_pre[i] = tmp;
    }

    for (int i = str.size() - 1; i ; --i) {
        int tmp = min((int)str.size() - i + 1, mx_suf[i + 1] + 2);
        while(pre_hash(i,i + tmp - 1) != suf_hash(i, i + tmp - 1)) --tmp;
        mx_suf[i] = tmp;
        mx = max(mx, mx_suf[i + 1] + mx_pre[i]);
    }
    cout << mx << endl;
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T = 1;
//    cin >> T;
    while (T--) {
        solve();
    }
    return 0;
}

矩阵哈希

参考洛谷P10474

#include <bits/stdc++.h>

#define LL long long
#define int long long
#define ULL unsigned long long
#define PII pair<LL,LL>
#define PCL pair<char,LL>
#define endl '\n'
using namespace std;

ULL base = 131, base2 = 113, h[1002][1002], p1[1005], p2[1005];

void init() {

}

set<ULL> st;

void solve() {
    int n, m, a, b;
    char ch;
    cin >> n >> m >> a >> b;
    p1[0] = p2[0] = 1;
    for (int i = 1; i < 1005; ++i) {
        p1[i] = p1[i - 1] * base;
        p2[i] = p2[i - 1] * base2;
    }

    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            cin >> ch;
            h[i][j] = h[i][j - 1] * base + (ULL) (ch - '0' + 1);
        }
    }

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; ++j) {
            h[i][j] = h[i - 1][j] * base2 + h[i][j];
        }
    }

    for (int i = a; i <= n; i++) {
        for (int j = a; j <= m; ++j) {
            st.insert(h[i][j] - h[i - a][j] * p2[a] - h[i][j - b] * p1[b] + h[i - a][j - b] * p1[b] * p2[a]);
        }
    }

    cin >> n;
    while (n--) {
        for (int i = 1; i <= a; ++i) {
            for (int j = 1; j <= b; ++j) {
                cin >> ch;
                h[i][j] = h[i][j - 1] * base + (ULL) (ch - '0' + 1);
            }
        }

        for (int i = 1; i <= a; i++) h[i][b] = h[i - 1][b] * base2 + h[i][b];

        cout << st.count(h[a][b]) << endl;
    }
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    init();
    int T = 1;
//    cin >> T;
    while (T--) {
        solve();
    }
    return 0;
}
Update date: 2025-05-14
Author: GuWolf
未解决问题: 暂无
最后修改:2025 年 08 月 06 日
如果觉得我的文章对你有用,请随意赞赏