题解 | 挑战赛 Round 89

A 但

知识点:构造、连通性、贪心

两种颜色都连通,且每列恰好选 $1$ 或 $2$ 个格子时,选中的格子只能整体贴着第 $1$ 行或第 $3$ 行延伸;如果在中途从上侧切到下侧,未选中的格子会被割断。

因此合法方案等价于:全选第 $1$ 行或全选第 $3$ 行,再从第 $2$ 行额外选 $k-n$ 个格子。若 $k2n$ 无解;否则取上下两行较大者,再加上中间行最大的 $k-n$ 个数即可。

时间复杂度 $\mathcal{O}(n\log n)$。

void solve() {
    int n, k;
    cin >> n >> k;

    vector<int> a;
    int s1 = 0, s3 = 0;
    for (int i = 0, x; i < n; ++i) cin >> x, s1 += x;
    for (int i = 0, x; i < n; ++i) cin >> x, a.push_back(x);
    for (int i = 0, x; i < n; ++i) cin >> x, s3 += x;

    if (k < n || k > 2 * n) return cout << -1 << endl, void();

    sort(a.rbegin(), a.rend());
    cout << max(s1, s3) + accumulate(a.begin(), a.begin() + k - n, 0LL) << endl;
}

B 鱼

知识点:搜索、命中集、剪枝

先把完全相同的集合去重,因为相同集合允许同时保留,且删除元素对它们的影响一致。

若当前未被删除的集合中存在两个不同集合相交,则这两个集合不能同时保留,所以任意可行删除集必须至少删除它们并集中的一个元素。于是每次找到一对冲突集合,在至多 $8$ 个候选元素中分支删除一个,搜索深度不超过 $3$。每找到一个合法方案,就按先删除数量、再字典序更新答案。

时间复杂度 $\mathcal{O}(n\log n+8^3n)$。

void solve() {
    int n, k;
    cin >> n >> k;

    vector<array<int, 4>> a(n);
    for (auto &v : a) {
        for (auto &x : v) cin >> x;
        sort(v.begin(), v.end());
    }

    sort(a.begin(), a.end());
    a.erase(unique(a.begin(), a.end()), a.end());
    n = a.size();

    vector<int> ans;
    int best = 4;

    unordered_map<int, int> mp;

    auto killed = [&](const array<int, 4> &v, const vector<int> &del) {
        for (auto x : v) {
            for (auto y : del) {
                if (x == y) return true;
            }
        }
        return false;
    };

    auto find = [&](const vector<int> &del) -> pii {
        mp.clear();
        for (int i = 0; i < n; ++i) {
            if (killed(a[i], del)) continue;

            for (auto x : a[i]) {
                if (mp.count(x)) {
                    return {i, mp[x]};
                }
            }

            for (auto x : a[i]) {
                mp[x] = i;
            }
        }
        return {-1, -1};
    };

    auto dfs = [&](auto self, vector<int> &del) -> void {
        if (del.size() > 3 || del.size() > best) return;

        auto [u, v] = find(del);

        if (u == -1) {
            auto cur = del;
            sort(cur.begin(), cur.end());

            if (cur.size() < best || (cur.size() == best && cur < ans)) {
                best = cur.size();
                ans = cur;
            }
            return;
        }

        if (del.size() == 3) return;

        vector<int> cand;
        for (auto x : a[u]) cand.push_back(x);
        for (auto x : a[v]) cand.push_back(x);

        sort(cand.begin(), cand.end());
        cand.erase(unique(cand.begin(), cand.end()), cand.end());

        for (auto x : cand) {
            del.push_back(x);
            self(self, del);
            del.pop_back();
        }
    };

    vector<int> del;
    dfs(dfs, del);
    if (best > 3) {
        cout << "NO" << endl;
    } else {
        cout << "YES " << best;
        for (auto x : ans) cout << " " << x;
        cout << endl;
    }
}

C 玩具

知识点:区间闭包、倍增 ST 表、贪心合并

把所有排列中的元素都转成它们在 $p_1$ 中的位置。一个最终段在所有排列中合法,等价于:它在每个排列中的位置集合都是连续区间。

对当前区间 $[L,R]$,不断做闭包:必须包含完整的初始段;在每个排列中,先求 $[L,R]$ 的元素出现位置范围 $[mn,mx]$,这个范围内出现的所有 $p_1$ 位置也必须被纳入新区间。用 ST 表维护区间最大最小值,即可快速扩展。

扫描 $p_1$​,每次取最小闭包作为一段;若扩展后与前一段相交,则它们必须合并,用栈弹出即可。这样得到的段数最多。

注意全开 $\texttt{long long}$ 会超空间。

时间复杂度 $\mathcal{O}(kn\log n)$。

template <class T> struct ST {...};

using i32 = int32_t;

void solve() {
    int n, k;
    cin >> n >> k;

    vector<int> id(n + 1);
    for (int i = 1, x; i <= n; i++) cin >> x, id[x] = i;

    vector<ST<i32>> pos, val;

    for (int r = 2; r <= k; r++) {
        vector<i32> a(n + 1), b(n + 1);

        for (int i = 1, x; i <= n; i++) cin >> x, a[i] = id[x], b[a[i]] = i;

        val.emplace_back(n);
        pos.emplace_back(n);

        val.back().init(a);
        pos.back().init(b);
    }

    int s;
    cin >> s;

    vector<i32> L0(n + 1), R0(n + 1);
    int cur = 0;

    for (int i = 1, x; i <= s; i++) {
        cin >> x;
        int l = cur + 1, r = cur + x;
        for (int j = l; j <= r; j++) L0[j] = l, R0[j] = r;
        cur = r;
    }

    vector<array<i32, 2>> ans;

    for (int i = 1; i <= n; i++) {
        if (!ans.empty() && i <= ans.back()[1]) continue;

        i32 L = i, R = i;

        while (true) {
            i32 nL = L0[L], nR = R0[R];

            for (int r = 2; r <= k; r++) {
                i32 mn = pos[r - 2].getMin(L, R), mx = pos[r - 2].getMax(L, R);

                nL = min(nL, val[r - 2].getMin(mn, mx));
                nR = max(nR, val[r - 2].getMax(mn, mx));
            }

            while (!ans.empty() && nL <= ans.back()[1]) {
                nL = min(nL, ans.back()[0]);
                nR = max(nR, ans.back()[1]);
                ans.pop_back();
            }

            if (nL == L && nR == R) break;

            L = nL, R = nR;
        }

        ans.push_back({L, R});
    }

    cout << ans.size() << endl;
    for (auto [l, r] : ans) {
        cout << r - l + 1 << " ";
    }
    cout << endl;
}

D 走失

知识点:按位独立、Fibonacci、异或变换

对每一位单独看,合法游走就是长度为 $K$、没有相邻两个 $1$ 的 01 串。设起始位为 $b$,特征位为 $x$,需要统计满足条件且异或为 $x$ 的串数,作为这一位上的转移系数。

若起始位为 $0$,总数为 $F_{K+1}$;若起始位为 $1$,总数为 $F_K$。再按异或奇偶拆开:

$$
\text{偶数个 }1=\frac{\text{总数}+\sum(-1)^{xor}}2,\qquad
\text{奇数个 }1=\frac{\text{总数}-\sum(-1)^{xor}}2
$$

其中 $\sum(-1)^{xor}$ 对 $K$ 呈周期 $6$。得到每一位上的 $2\times 2$ 转移后,对数组做类似 $\texttt{FWT}$ 的逐位变换即可。

为了美观,这里使用矩阵快速幂求斐波那契数列,倍增求亦可。

时间复杂度 $\mathcal{O}(n2^n+\log K)$。

int _1[] = {1, 0, -1, -1, 0, 1}, _2[] = {-1, -1, 0, 1, 1, 0};

void solve() {
    int n, k;
    cin >> n >> k;

    int siz = 1LL << n;
    vector<Z> a(siz);
    for (auto &v : a) cin >> v;

    Matrix<Z> F(2, 2, 1);
    F[1][1] = 0;
    F = F.ksm(k);

    Z s1 = _1[(k - 1) % 6], s2 = _2[(k - 1) % 6];

    Z q00 = (F[0][0] + s1) / 2, q01 = (F[0][1] + s2) / 2, q10 = (F[0][0] - s1) / 2, q11 = (F[0][1] - s2) / 2;

    for (int len = 1; len < siz; len <<= 1) {
        for (int i = 0; i < siz; i += 2 * len) {
            for (int j = 0; j < len; ++j) {
                Z u = a[i + j], v = a[i + len + j];
                a[i + j] = q00 * u + q01 * v;
                a[i + len + j] = q10 * u + q11 * v;
            }
        }
    }

    int ans = 0;
    for (int i = 0; i < siz; ++i) ans ^= (a[i] * (i + 1)).val();

    cout << ans << endl;
}

倍增求斐波那契数列:

auto fib = [&](auto &&self, int n) -> array<Z, 2> {
    if (n == 0) return {0, 1};

    auto [a, b] = self(self, n >> 1);

    Z c = a * (2 * b - a), d = a * a + b * b;

    if (n & 1) return {d, c + d};
    return {c, d};
};
auto [fk, fk1] = fib(fib, k);

Z s1 = _1[(k - 1) % 6], s2 = _2[(k - 1) % 6];

Z q00 = (fk1 + s1) / 2, q01 = (fk + s2) / 2, q10 = (fk1 - s1) / 2, q11 = (fk - s2) / 2;

E 芳草地

知识点:后缀自动机、贡献转换、根号分治、离线并查集

把贡献按 $B$ 的每次出现来数。若某个出现的右端点为 $j$,取后缀长度 $l$ 作为 $A$,则可作为 $B$ 的长度有 $l,2l,3l,\ldots\le j$,数量为 $\left\lfloor j/l\right\rfloor$。所以答案等价于:

$$
\sum_{j=1}^n\sum_{l=1}^j occ(S[j-l+1..j])\left\lfloor\frac jl\right\rfloor
$$

用 $\texttt{SAM}$ 求任意子串出现次数。短长度 $l\le B$ 直接枚举所有出现。长长度部分设

$$
G_j(x)=\sum_{l=1}^{x}occ(S[j-l+1..j])
$$

令 $m=\left\lfloor\dfrac{j}{B+1}\right\rfloor$,则所有 $l>B$ 的贡献可以写成:

$$
G_j(j)-mG_j(B)+\sum_{q=2}^{m}G_j\left(\left\lfloor\frac{j}{q}\right\rfloor\right)
$$

因为每个 $l>B$ 的系数正好是 $1+#{q\ge2:q\le\lfloor j/l\rfloor}=\lfloor j/l\rfloor$,而 $l\le B$ 的系数会被 $-mG_j(B)$ 抵消。

接下来只需要快速回答 $G_j(x)$。将询问按 $x$ 分组,按长度从大到小在 $\texttt{SAM}$ 后缀树上激活状态并用并查集合并,查询时就能找到表示长度 $x$ 后缀的状态,从而用后缀链前缀和计算 $G_j(x)$。

时间复杂度 $\mathcal{O}(nB+\frac{n^2}{B}\alpha(n))$。

template <int ALPHABET_SIZE = 26> struct SAM {...};

void solve() {
    string s;
    cin >> s;
    int n = s.size();

    SAM sam;
    int last = 1;
    vector<int> pos(n + 1), cnt(2 * n + 5);

    for (int i = 1; i <= n; ++i) {
        last = sam.extend(last, s[i - 1]);
        ++cnt[last];
        pos[i] = last;
    }

    int sz = sam.size();
    cnt.resize(sz);

    vector<int> ord(sz);
    iota(ord.begin(), ord.end(), 0);
    sort(ord.begin(), ord.end(), [&](int a, int b) { return sam.len(a) < sam.len(b); });

    for (int i = sz - 1; i >= 0; --i) {
        int v = ord[i];
        if (v > 1) cnt[sam.link(v)] += cnt[v];
    }

    vector<Z> pre(sz);
    for (auto v : ord) {
        if (v <= 1) continue;
        int f = sam.link(v);
        pre[v] = pre[f] + Z(cnt[v]) * (sam.len(v) - sam.len(f));
    }

    int B = min(700LL, n);
    Z ans = 0;

    for (int i = 0; i < n; ++i) {
        int v = 1;
        for (int d = 1; d <= B && i + d <= n; ++d) {
            v = sam.next(v, s[i + d - 1]);
            ans += Z(cnt[v]) * ((i + d) / d);
        }
    }

    vector<int> num(n + 1), off(n + 2), cur(n + 1);

    for (int j = B + 1; j <= n; ++j) {
        int m = j / (B + 1);
        ++num[B];
        for (int q = 2; q <= m; ++q) {
            ++num[j / q];
        }
    }

    for (int i = 0; i <= n; ++i) {
        off[i + 1] = off[i] + num[i];
    }

    vector<array<int, 2>> qs(off[n + 1]);
    cur = off;

    for (int j = B + 1; j <= n; ++j) {
        int m = j / (B + 1);

        ans += pre[pos[j]];

        qs[cur[B]++] = {pos[j], -m};
        for (int q = 2; q <= m; ++q) {
            qs[cur[j / q]++] = {pos[j], 1LL};
        }
    }

    vector<int> hl(n + 1, -1), nl(sz, -1);
    vector<int> hs(sz, -1), ns(sz, -1);

    for (int v = 2; v < sz; ++v) {
        nl[v] = hl[sam.len(v)];
        hl[sam.len(v)] = v;

        int f = sam.link(v);
        ns[v] = hs[f];
        hs[f] = v;
    }

    vector<int> fa(sz);
    vector<bool> vis(sz);

    auto find = [&](int x) {
        int r = x;
        while (fa[r] != r) r = fa[r];
        while (fa[x] != x) {
            int y = fa[x];
            fa[x] = r;
            x = y;
        }
        return r;
    };

    for (int x = n; x >= 1; --x) {
        for (int u = hl[x]; u != -1; u = nl[u]) {
            vis[u] = 1;
            fa[u] = u;

            for (int e = hs[u]; e != -1; e = ns[e]) {
                if (vis[e]) fa[find(e)] = u;
            }
        }

        for (int id = off[x]; id < off[x + 1]; ++id) {
            int u = find(qs[id][0]);
            int f = sam.link(u);
            ans += Z(qs[id][1]) * (pre[f] + Z(cnt[u]) * (x - sam.len(f)));
        }
    }

    cout << ans << endl;
}

F 莉莉安

知识点:树上匹配、凸函数、Slope Trick、左偏树

固定边长后,黑白叶子的最小匹配代价可以按边贡献计算。设一条边下方黑白叶数量差为 $bal$,则这条边必然被跨过 $|bal|$ 次,因此匹配代价贡献为:

$$
|bal|\cdot x_e
$$

于是目标变为:

$$
\sum_e |x_e-a_e|+|bal_e|x_e
$$

并要求所有叶子到根距离相等。

对每个点 $u$ 维护函数 $f_u(d)$:使 $u$ 子树内所有叶子到 $u$ 的距离都为 $d$ 的最小代价。多个儿子合并时就是函数相加;向父亲转移时,要加上父边的代价并做一次凸函数变换。

这些函数都是分段线性凸函数,可以用 $\texttt{slope trick}$ 维护:$base$ 表示当前值,$L$ 表示初始斜率,左偏树维护斜率变化的断点。子树合并就是合并堆,经过一条边时根据 $a_e$ 和 $|bal|$ 调整断点。最后根节点没有父边,只需在 $d\ge0$ 上取 $f_1(d)$ 的最小值。

时间复杂度 $\mathcal{O}(n\log n)$。

template <class T, class Cmp = less<T>> struct LeftistHeap {...};

void solve() {
    int n;
    cin >> n;
    vector<vector<pii>> g(n + 1);
    for (int i = 1; i < n; ++i) {
        int u, v, a;
        cin >> u >> v >> a;
        g[u].push_back({v, a});
        g[v].push_back({u, a});
    }
    vector<int> col(n + 1);
    for (int i = 1; i <= n; ++i) cin >> col[i];

    vector<int> fa(n + 1), w(n + 1), ord;
    fa[1] = -1;
    ord.push_back(1);

    for (int i = 0; i < ord.size(); ++i) {
        int u = ord[i];
        for (auto [v, a] : g[u]) {
            if (v == fa[u]) continue;
            fa[v] = u;
            w[v] = a;
            ord.push_back(v);
        }
    }

    vector<int> bal(n + 1), base(n + 1), L(n + 1);
    LeftistHeap<int> q(2 * n + 5);

    auto trans = [&](int u, int a, int b) {
        int l = b - 1, r = b + 1, l0 = L[u];
        base[u] += a;

        if (a == 0) {
            if (l0 < r) {
                int k = r - l0;
                while (q[u].size() > k) q[u].pop();
            } else {
                q[u].clear();
                L[u] = r;
            }
            return;
        }

        if (l0 <= l) {
            int k = r - l0;
            while (q[u].size() > k) q[u].pop();
            int x = q[u].pop(), y = q[u].pop();
            q[u] << x + a << y + a;
        } else {
            if (l0 == l + 1) {
                while (q[u].size() > 1) q[u].pop();
                int x = q[u].pop();
                q[u].clear();
                q[u] << a << x + a;
            } else {
                q[u].clear();
                q[u] << a << a;
            }
            L[u] = l;
        }
    };

    for (int i = n - 1; i >= 0; --i) {
        int u = ord[i];
        bool leaf = true;

        for (auto [v, a] : g[u]) {
            if (fa[v] != u) continue;
            leaf = false;
            bal[u] += bal[v];
            base[u] += base[v];
            L[u] += L[v];
            q[u] += q[v];
        }

        if (leaf) {
            bal[u] = col[u];
            if (u != 1) {
                base[u] = w[u];
                L[u] = 0;
                q[u] << w[u] << w[u];
            }
        } else if (u != 1) {
            trans(u, w[u], abs(bal[u]));
        }
    }

    vector<int> p;
    while (!q[1].empty()) p.push_back(q[1].pop());
    reverse(p.begin(), p.end());

    int ans = base[1], lst = 0;
    for (int i = 0; i < p.size(); ++i) {
        int s = L[1] + i;
        int len = p[i] - lst;
        if (s < 0) ans += s * len;
        lst = p[i];
    }
    cout << ans << endl;
}

约定

#include <bits/stdc++.h>
using namespace std;

#define int long long
#define pii array<int, 2>
#define endl "\n"

void solve() {}

signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    int t = 1;
    cin >> t;
    cout << fixed << setprecision(20);
    for (int i = 0; i < t; ++i) {
        solve();
    }
    return 0;
}

ST表

template <class T> struct ST {
    int n, k;
    vector<vector<T>> Max;
    vector<vector<T>> Min;

    ST(int n) : n(n), k(__lg(n)) {
        Max.resize(k + 1, vector<T>(n + 1));
        Min.resize(k + 1, vector<T>(n + 1));
    }

    template <class Array> void init(const Array &a) {
        for (int i = 1; i <= n; i++) {
            Max[0][i] = a[i];
            Min[0][i] = a[i];
        }
        for (int i = 0, t = 1; i < k; i++, t <<= 1) {
            const int lim = n - (t << 1) + 1;
            for (int j = 1; j <= lim; j++) {
                Max[i + 1][j] = max(Max[i][j], Max[i][j + t]);
                Min[i + 1][j] = min(Min[i][j], Min[i][j + t]);
            }
        }
    }

    T getMax(int l, int r) {
        if (l > r) swap(l, r);
        int k = __lg(r - l + 1);
        return max(Max[k][l], Max[k][r - (1LL << k) + 1]);
    }

    T getMin(int l, int r) {
        if (l > r) swap(l, r);
        int k = __lg(r - l + 1);
        return min(Min[k][l], Min[k][r - (1LL << k) + 1]);
    }
};

矩阵

template <class T> struct Matrix {
    int n, m;
    vector<vector<T>> a;

    Matrix(int n = 0, int m = 0, T val = T()) : n(n), m(m), a(n, vector<T>(m, val)) {}

    static Matrix identity(int n) {
        Matrix I(n, n);
        for (int i = 0; i < n; i++) I.a[i][i] = T(1);
        return I;
    }

    vector<T> &operator[](int i) {
        return a[i];
    }
    const vector<T> &operator[](int i) const {
        return a[i];
    }

    Matrix operator+(const Matrix &other) const {
        assert(n == other.n && m == other.m);
        Matrix res(n, m);
        for (int i = 0; i < n; i++)
            for (int j = 0; j < m; j++) res[i][j] = a[i][j] + other[i][j];
        return res;
    }

    Matrix operator-(const Matrix &other) const {
        assert(n == other.n && m == other.m);
        Matrix res(n, m);
        for (int i = 0; i < n; i++)
            for (int j = 0; j < m; j++) res[i][j] = a[i][j] - other[i][j];
        return res;
    }

    Matrix operator*(const Matrix &other) const {
        assert(m == other.n);
        Matrix res(n, other.m, T(0));
        for (int i = 0; i < n; i++)
            for (int k = 0; k < m; k++)
                for (int j = 0; j < other.m; j++) res[i][j] = res[i][j] + a[i][k] * other[k][j];
        return res;
    }

    Matrix operator*(const T &k) const {
        Matrix res(n, m);
        for (int i = 0; i < n; i++)
            for (int j = 0; j < m; j++) res[i][j] = a[i][j] * k;
        return res;
    }

    Matrix transpose() const {
        Matrix res(m, n);
        for (int i = 0; i < n; i++)
            for (int j = 0; j < m; j++) res[j][i] = a[i][j];
        return res;
    }

    Matrix ksm(int exp) const {
        assert(n == m);
        Matrix base = *this;
        Matrix res = identity(n);
        while (exp > 0) {
            if (exp & 1) res = res * base;
            base = base * base;
            exp >>= 1;
        }
        return res;
    }
};

template <class T> T determinant(int n, Matrix<T> &mat) {
    T det = T(1);
    int sign = 1;

    for (int i = 0; i < n; i++) {
        int pivot = i;
        while (pivot < n && mat[pivot][i].isZero()) pivot++;
        if (pivot == n) return T();

        if (pivot != i) {
            swap(mat[i], mat[pivot]);
            sign = -sign;
        }

        det *= mat[i][i];
        for (int j = i + 1; j < n; j++) {
            if (!mat[j][i].isZero()) {
                T factor = mat[j][i] / mat[i][i];
                for (int k = i; k < n; k++) {
                    mat[j][k] -= factor * mat[i][k];
                }
            }
        }
    }

    if (sign == -1) {
        det = T() - det;
    }
    return det;
}

自动取模

constexpr int MOD = 998244353;

template <class T> constexpr T mypow(T a, int b) {
    T res = 1;
    while (b) {
        if (b & 1) res *= a;
        b >>= 1;
        a *= a;
    }
    return res;
}

template <int P> struct MInt {
    int x;
    constexpr MInt() : x() {}
    constexpr MInt(int x) : x{norm(x % getMod())} {}
    static int Mod;
    constexpr static int getMod() {
        if (P > 0) {
            return P;
        } else {
            return Mod;
        }
    }
    constexpr static void setMod(int Mod_) {
        Mod = Mod_;
    }
    constexpr int norm(int x) const {
        if (x < 0) {
            x += getMod();
        }
        if (x >= getMod()) {
            x -= getMod();
        }
        return x;
    }
    constexpr int val() const {
        return x;
    }
    explicit constexpr operator int() const {
        return x;
    }
    constexpr MInt operator-() const {
        MInt res;
        res.x = norm(getMod() - x);
        return res;
    }
    constexpr MInt inv() const {
        assert(x != 0);
        return mypow(*this, getMod() - 2);
    }
    constexpr MInt &operator*=(MInt rhs) & {
        x = 1ll * x * rhs.x % getMod();
        return *this;
    }
    constexpr MInt &operator+=(MInt rhs) & {
        x = norm(x + rhs.x);
        return *this;
    }
    constexpr MInt &operator-=(MInt rhs) & {
        x = norm(x - rhs.x);
        return *this;
    }
    constexpr MInt &operator/=(MInt rhs) & {
        return *this *= rhs.inv();
    }
    friend constexpr MInt operator*(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res *= rhs;
        return res;
    }
    friend constexpr MInt operator+(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res += rhs;
        return res;
    }
    friend constexpr MInt operator-(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res -= rhs;
        return res;
    }
    friend constexpr MInt operator/(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res /= rhs;
        return res;
    }
    friend constexpr istream &operator>>(istream &is, MInt &a) {
        int v;
        is >> v;
        a = MInt(v);
        return is;
    }
    friend constexpr ostream &operator<<(ostream &os, const MInt &a) {
        return os << a.val();
    }
    friend constexpr bool operator==(MInt lhs, MInt rhs) {
        return lhs.val() == rhs.val();
    }
    friend constexpr bool operator!=(MInt lhs, MInt rhs) {
        return lhs.val() != rhs.val();
    }
};

template <> int MInt<0>::Mod = 998244353;

template <int V, int P> constexpr MInt<P> CInv = MInt<P>(V).inv();

using Z = MInt<MOD>;

后缀自动机

template <int ALPHABET_SIZE = 26> struct SAM {
    struct Node {
        int len;
        int link;
        array<int, ALPHABET_SIZE> next;
        Node() : len{}, link{}, next{} {}
    };
    vector<Node> t;
    SAM() {
        init();
    }
    void init() {
        t.assign(2, Node());
        t[0].next.fill(1);
        t[0].len = -1;
    }
    int newNode() {
        t.emplace_back();
        return t.size() - 1;
    }
    int extend(int p, int c) {
        if (t[p].next[c]) {
            int q = t[p].next[c];
            if (t[q].len == t[p].len + 1) {
                return q;
            }
            int r = newNode();
            t[r].len = t[p].len + 1;
            t[r].link = t[q].link;
            t[r].next = t[q].next;
            t[q].link = r;
            while (t[p].next[c] == q) {
                t[p].next[c] = r;
                p = t[p].link;
            }
            return r;
        }
        int cur = newNode();
        t[cur].len = t[p].len + 1;
        while (!t[p].next[c]) {
            t[p].next[c] = cur;
            p = t[p].link;
        }
        t[cur].link = extend(p, c);
        return cur;
    }
    int extend(int p, char c, char offset = 'a') {
        return extend(p, c - offset);
    }

    int next(int p, int x) {
        return t[p].next[x];
    }

    int next(int p, char c, char offset = 'a') {
        return next(p, c - offset);
    }

    int link(int p) {
        return t[p].link;
    }

    int len(int p) {
        return t[p].len;
    }

    int size() {
        return t.size();
    }
};

左偏堆

template <class T, class Cmp = less<T>> struct LeftistHeap {
    struct Node {
        T v;
        int l, r, d;
    };

    vector<Node> tr;
    vector<int> rt, siz;
    Cmp cmp;
    int tot;

    LeftistHeap() {}
    LeftistHeap(int n) {
        init(n);
    }

    void init(int n) {
        tr.assign(1, {});
        rt.assign(n + 1, 0);
        siz.assign(n + 1, 0);
        tot = 0;
    }

    int mergeRoot(int x, int y) {
        if (!x || !y) return x | y;
        if (cmp(tr[x].v, tr[y].v)) swap(x, y);
        tr[x].r = mergeRoot(tr[x].r, y);
        if (tr[tr[x].l].d < tr[tr[x].r].d) swap(tr[x].l, tr[x].r);
        tr[x].d = tr[tr[x].r].d + 1;
        return x;
    }

    void merge(int x, int y) {
        if (x == y) return;
        rt[x] = mergeRoot(rt[x], rt[y]);
        siz[x] += siz[y];
        rt[y] = siz[y] = 0;
    }

    void push(int x, const T &v) {
        tr.push_back({v, 0, 0, 1});
        rt[x] = mergeRoot(rt[x], ++tot);
        ++siz[x];
    }

    T top(int x) {
        return tr[rt[x]].v;
    }

    T pop(int x) {
        int u = rt[x];
        T res = tr[u].v;
        rt[x] = mergeRoot(tr[u].l, tr[u].r);
        --siz[x];
        return res;
    }

    void clear(int x) {
        rt[x] = siz[x] = 0;
    }

    bool empty(int x) {
        return siz[x] == 0;
    }

    int size(int x) {
        return siz[x];
    }

    struct Ref {
        LeftistHeap *h;
        int x;

        Ref &operator+=(Ref y) {
            h->merge(x, y.x);
            return *this;
        }

        Ref &operator<<(const T &v) {
            h->push(x, v);
            return *this;
        }

        T top() {
            return h->top(x);
        }

        T pop() {
            return h->pop(x);
        }

        void clear() {
            h->clear(x);
        }

        bool empty() {
            return h->empty(x);
        }

        int size() {
            return h->size(x);
        }
    };

    Ref operator[](int x) {
        return {this, x};
    }
};
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇