LOADING

加载过慢请开启缓存 浏览器默认开启

算法竞赛快速复习板子(持续更新)

2025/2/20 算法

并查集

(Disjoint Set Union)

struct DSU {
    std::vector<int> f, siz;
    
    DSU() {}
    DSU(int n) {
        init(n);
    }
    
    void init(int n) {
        f.resize(n);
        std::iota(f.begin(), f.end(), 0);
        siz.assign(n, 1);
    }
    
    int find(int x) {
        while (x != f[x]) {
            x = f[x] = f[f[x]];
        }
        return x;
    }
    
    bool same(int x, int y) {
        return find(x) == find(y);
    }
    
    bool merge(int x, int y) {
        x = find(x);
        y = find(y);
        if (x == y) {
            return false;
        }
        siz[x] += siz[y];
        f[y] = x;
        return true;
    }
    
    int size(int x) {
        return siz[find(x)];
    }
};

树状数组

Fenwick Tree

struct FenwickTree {
    vector<int> tree;
    int n;

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

    void init(int n) {
        this->n = n;
        tree.resize(n + 1, 0);
    }

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

    void update(int x, int k) {
        while (x <= n) {
            tree[x] += k;
            x += lowbit(x);
        }
    }

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

    int query(int l, int r) {
        return query(r) - query(l - 1);
    }
};

类欧几里德算法解决Floor_Sum问题

Floor_Sum

问题解析

constexpr long long MOD = 998244353;
constexpr long long INV_2 = 499122177;  // 2的逆元
constexpr long long INV_6 = 166374059;  // 6的逆元

struct Floor_sum {
    long long f, g, h;  // 三个函数值

    Floor_sum(long long f = 0, long long g = 0, long long h = 0) : f(f), g(g), h(h) {}

    // 重载加法运算符,方便合并结果
    Floor_sum operator+(const Floor_sum& other) const {
        return Floor_sum((f + other.f), (g + other.g), (h + other.h));
    }
};

// 单独封装 f 的计算逻辑
long long calculate_f(long long n, long long a, long long b, long long c, bool is_mod = true) {
    if (a == 0) {
        return is_mod ? ((b / c) * (n + 1) % MOD) : ((b / c) * (n + 1));
    }
    if (a >= c || b >= c) {
        long long ac = a / c, bc = b / c;
        long long base_f = is_mod ? ((ac * n % MOD * (n + 1) % MOD * INV_2 + bc * (n + 1)) % MOD) : (ac * n * (n + 1) / 2 + bc * (n + 1));
        return is_mod ? ((base_f + calculate_f(n, a % c, b % c, c, true)) % MOD) : (base_f + calculate_f(n, a % c, b % c, c, false));
    }
    long long m = (a * n + b) / c;
    return is_mod ? ((n * m % MOD - calculate_f(m - 1, c, c - b - 1, a, true) + MOD) % MOD) : (n * m - calculate_f(m - 1, c, c - b - 1, a, false));
}

// 封装 g 和 h 的计算逻辑,依赖 f 的结果
Floor_sum calculate_gh(long long n, long long a, long long b, long long c, long long f, bool is_mod = true) {
    if (a == 0) {
        long long bc = b / c;
        return is_mod ? Floor_sum(bc * n % MOD * (n + 1) % MOD * INV_2 % MOD, bc * bc % MOD * (n + 1) % MOD) : Floor_sum(bc * n * (n + 1) / 2, bc * bc * (n + 1));
    }
    if (a >= c || b >= c) {
        long long ac = a / c, bc = b / c;
        Floor_sum base(is_mod ? (ac * n % MOD * (n + 1) % MOD * (2 * n + 1) % MOD * INV_6 % MOD + bc * n % MOD * (n + 1) % MOD * INV_2 % MOD) : (ac * n * (n + 1) * (2 * n + 1) / 6 + bc * n * (n + 1) / 2),
                       is_mod ? (ac * ac % MOD * n % MOD * (n + 1) % MOD * (2 * n + 1) % MOD * INV_6 % MOD + bc * bc % MOD * (n + 1) % MOD + ac * bc % MOD * n % MOD * (n + 1) % MOD) : (ac * ac * n * (n + 1) * (2 * n + 1) / 6 + bc * bc * (n + 1) + ac * bc * n * (n + 1)));
        Floor_sum recursive = calculate_gh(n, a % c, b % c, c, f, is_mod);
        return is_mod ? Floor_sum((base.g + recursive.g) % MOD, (base.h + recursive.h + 2 * bc % MOD * recursive.f % MOD + 2 * ac % MOD * recursive.g % MOD) % MOD) : Floor_sum(base.g + recursive.g, base.h + recursive.h + 2 * bc * recursive.f + 2 * ac * recursive.g);
    }
    long long m = (a * n + b) / c;
    Floor_sum recursive = calculate_gh(m - 1, c, c - b - 1, a, f, is_mod);
    return is_mod ? Floor_sum((m * n % MOD * (n + 1) % MOD - recursive.h - recursive.f + MOD) % MOD * INV_2 % MOD,
                              (n * m % MOD * (m + 1) % MOD - 2 * recursive.g - 2 * recursive.f - recursive.f + MOD) % MOD) : Floor_sum((m * n * (n + 1) - recursive.h - recursive.f) / 2,
                                                                                                                               n * m * (m + 1) - 2 * recursive.g - 2 * recursive.f - recursive.f);
}

// 主计算函数,整合 f、g、h 的计算
Floor_sum calculate(long long n, long long a, long long b, long long c, bool is_mod = true) {
    long long f = calculate_f(n, a, b, c, is_mod);
    Floor_sum gh = calculate_gh(n, a, b, c, f, is_mod);
    return Floor_sum(f, gh.g, gh.h);
}

最大流

(Dinic)

const int INF = 1e9 + 10;
const ll INF_LL = 4e18;

// 最大流 Dinic 算法实现
struct MXF {
    struct Edge {
        int from, to, rev; // 边的起点、终点和反向边索引
        ll cap, flow;      // 边的容量和流量
        bool isrev;        // 是否是反向边
        Edge(int from, int to, ll cap, int rev, bool isrev)
            : from(from), to(to), rev(rev), cap(cap), flow(0), isrev(isrev) {}
    };

    vector<vector<Edge>> graph; // 邻接表
    vector<int> level, iter;    // BFS 层次和 DFS 当前弧

    // 构造函数,初始化图
    MXF(int n) : graph(n), level(n), iter(n) {}

    // 添加边
    void add_edge(int from, int to, ll cap) {
        graph[from].emplace_back(from, to, cap, graph[to].size(), false);
        graph[to].emplace_back(to, from, 0, graph[from].size() - 1, true);
    }

    // BFS 构建层次图
    void bfs(int s) {
        fill(level.begin(), level.end(), -1);
        queue<int> q;
        level[s] = 0;
        q.push(s);
        while (!q.empty()) {
            int v = q.front();
            q.pop();
            for (auto &e : graph[v]) {
                if (e.cap > 0 && level[e.to] < 0) {
                    level[e.to] = level[v] + 1;
                    q.push(e.to);
                }
            }
        }
    }

    // DFS 寻找增广路径
    ll dfs(int v, int t, ll f) {
        if (v == t) return f;
        for (int &i = iter[v]; i < graph[v].size(); ++i) {
            Edge &e = graph[v][i];
            if (e.cap > 0 && level[v] < level[e.to]) {
                ll d = dfs(e.to, t, min(f, e.cap));
                if (d > 0) {
                    e.cap -= d;
                    graph[e.to][e.rev].cap += d;
                    e.flow += d;
                    graph[e.to][e.rev].flow -= d;
                    return d;
                }
            }
        }
        return 0;
    }

    // 计算从源点 s 到汇点 t 的最大流
    ll flow(int s, int t) {
        ll ret = 0;
        while (true) {
            bfs(s);
            if (level[t] < 0) return ret; // 如果汇点不可达,结束算法
            fill(iter.begin(), iter.end(), 0);
            ll f;
            while ((f = dfs(s, t, INF_LL)) > 0) ret += f;
        }
    }

    // 获取最小割
    vector<bool> mincut(int v = 0) {
        vector<bool> ret(graph.size());
        queue<int> q;
        q.push(v);
        ret[v] = true;
        while (!q.empty()) {
            int v = q.front();
            q.pop();
            for (auto &e : graph[v]) {
                if (e.cap > 0 && !ret[e.to]) {
                    ret[e.to] = true;
                    q.push(e.to);
                }
            }
        }
        return ret;
    }

    // 获取所有边的信息
    vector<Edge> get_edges() {
        vector<Edge> ret;
        for (int i = 0; i < graph.size(); ++i) {
            for (auto &e : graph[i]) {
                if (e.isrev) continue;
                ret.push_back(e);
            }
        }
        return ret;
    }
};

最小费用流

(MinCostFlow)

const int INF = 1e9 + 10;
const ll INF_LL = 4e18;

// 最小費用流
// 支持负权边,但不允许存在负环。
struct MCF {
    struct Edge {
        int from, to, rev;
        ll cap, cost;
        bool isrev;
        Edge(int from, int to, ll cap, ll cost, int rev, bool isrev) : from(from), to(to), cap(cap), cost(cost), rev(rev), isrev(isrev) {}
    };

    vector<vector<Edge>> graph;
    vector<ll> dist, pot;  // dist: 最短距离,pot: 势能函数
    vector<int> pv, pe;

    MCF(int n) { graph.resize(n), dist.resize(n), pot.resize(n), pv.resize(n), pe.resize(n); }

    // 添加边
    // 增加一条从 from 到 to 的边,容量为 cap,费用为 cost
    // cost 可以为负,但图中不能有负环
    void add_edge(int from, int to, ll cap, ll cost) {
        graph[from].push_back(Edge(from, to, cap, cost, graph[to].size(), false));
        graph[to].push_back(Edge(to, from, 0, -cost, (int)graph[from].size() - 1, true));
    }

    // 获取所有边的信息
    vector<Edge> get_edges() {
        vector<Edge> ret;
        for (int i = 0; i < (int)graph.size(); i++)
            for (auto &e : graph[i])
                if (!e.isrev) ret.push_back(e);
        return ret;
    }

    // 计算最小费用流
    // 从源点 s 到汇点 t,寻找流量为 f 的最小费用流
    // 如果无法满足流量 f,则返回 INFL 表示无解
    // 时间复杂度:O(FE log V),其中 F 是流量,E 是边数,V 是顶点数
    ll flow(int s, int t, ll f) {
        int n = graph.size();
        priority_queue<pair<ll, int>, vector<pair<ll, int>>, greater<pair<ll, int>>> pq;
        fill(pot.begin(), pot.end(), 0), fill(pv.begin(), pv.end(), 0), fill(pe.begin(), pe.end(), 0);
        ll ret = 0;

        while (f > 0) {
            fill(dist.begin(), dist.end(), INF_LL);
            dist[s] = 0;
            pq.push({0, s});
            while (!pq.empty()) {
                auto [tmp, now] = pq.top();
                pq.pop();
                if (dist[now] < tmp) continue;
                for (int i = 0; i < (int)graph[now].size(); i++) {
                    auto [from, to, rev, cap, cost, isrev] = graph[now][i];
                    ll ncost = dist[now] + cost + pot[now] - pot[to];
                    if (cap > 0 && dist[to] > ncost) {
                        dist[to] = ncost, pv[to] = now, pe[to] = i;
                        pq.push({dist[to], to});
                    }
                }
            }
            if (dist[t] == INF_LL) return INF_LL;
            for (int i = 0; i < n; i++) pot[i] += dist[i];
            ll d = f;
            for (int v = t; v != s; v = pv[v]) d = min(d, graph[pv[v]][pe[v]].cap);
            f -= d, ret += d * pot[t];
            for (int v = t; v != s; v = pv[v]) {
                auto &e = graph[pv[v]][pe[v]];
                e.cap -= d, graph[v][e.rev].cap += d;
            }
        }

        return ret;
    }
};

卷积

const int INF = 1e9 + 10;
const ll INFL = 4e18;

template <ll MOD>
struct ModInt {
    ll value;
    ModInt(ll x = 0) { value = (x >= 0 ? x % MOD : MOD - (-x) % MOD); }
    ModInt operator-() const { return ModInt(-value); }
    ModInt operator+() const { return ModInt(*this); }
    ModInt& operator+=(const ModInt& other) {
        value += other.value;
        if (value >= MOD) value -= MOD;
        return *this;
    }
    ModInt& operator-=(const ModInt& other) {
        value += MOD - other.value;
        if (value >= MOD) value -= MOD;
        return *this;
    }
    ModInt& operator*=(const ModInt other) {
        value = value * other.value % MOD;
        return *this;
    }
    ModInt& operator/=(ModInt other) {
        (*this) *= other.inv();
        return *this;
    }
    ModInt operator+(const ModInt& other) const { return ModInt(*this) += other; }
    ModInt operator-(const ModInt& other) const { return ModInt(*this) -= other; }
    ModInt operator*(const ModInt& other) const { return ModInt(*this) *= other; }
    ModInt operator/(const ModInt& other) const { return ModInt(*this) /= other; }
    ModInt pow(ll x) const {
        ModInt ret(1), mul(value);
        while (x) {
            if (x & 1) ret *= mul;
            mul *= mul;
            x >>= 1;
        }
        return ret;
    }
    ModInt inv() const { return pow(MOD - 2); }
    bool operator==(const ModInt& other) const { return value == other.value; }
    bool operator!=(const ModInt& other) const { return value != other.value; }
    friend ostream& operator<<(ostream& os, const ModInt& x) { return os << x.value; }
    friend istream& operator>>(istream& is, ModInt& x) {
        ll v;
        is >> v;
        x = ModInt<MOD>(v);
        return is;
    }
    static constexpr ll get_mod() { return MOD; }
};
using Mod998 = ModInt<998244353>;
using Mod107 = ModInt<1000000007>;

vector<Mod998> NTT998(vector<Mod998> a, bool inv = false) {
    int n = a.size(), h = 0;
    while ((1 << h) < n) h++;
    for (int i = 0; i < n; i++) {
        int j = 0;
        for (int k = 0; k < h; k++) j |= (i >> k & 1) << (h - 1 - k);
        if (i < j) swap(a[i], a[j]);
    }

    const vector<Mod998> rt = {1, 998244352, 911660635, 372528824, 929031873, 452798380, 922799308, 781712469, 476477967, 166035806, 258648936, 584193783, 63912897, 350007156, 666702199, 968855178, 629671588, 24514907, 996173970, 363395222, 565042129, 733596141, 267099868, 15311432};
    const vector<Mod998> irt = {1, 998244352, 86583718, 509520358, 337190230, 87557064, 609441965, 135236158, 304459705, 685443576, 381598368, 335559352, 129292727, 358024708, 814576206, 708402881, 283043518, 3707709, 121392023, 704923114, 950391366, 428961804, 382752275, 469870224};

    for (int b = 1, t = 1; b < n; b <<= 1, t++) {
        Mod998 w = 1, base = inv ? irt[t] : rt[t];
        for (int j = 0; j < b; j++) {
            for (int k = 0; k < n; k += b * 2) {
                Mod998 s = a[j + k];
                Mod998 t = a[j + k + b] * w;
                a[j + k] = s + t;
                a[j + k + b] = s - t;
            }
            w *= base;
        }
    }

    if (inv) {
        Mod998 inv_n = Mod998(n).inv();
        for (int i = 0; i < n; i++) a[i] *= inv_n;
    }

    return a;
}

vector<Mod998> convolve998(vector<Mod998> a, vector<Mod998> b) {
    int n = 1;
    while (n < (int)a.size() + (int)b.size() - 1) n <<= 1;
    vector<Mod998> fa(n), fb(n);
    for (int i = 0; i < (int)a.size(); i++) fa[i] = a[i];
    for (int i = 0; i < (int)b.size(); i++) fb[i] = b[i];
    fa = NTT998(fa), fb = NTT998(fb);
    for (int i = 0; i < n; i++) fa[i] *= fb[i];
    fa = NTT998(fa, true);
    while ((int)fa.size() > (int)a.size() + (int)b.size() - 1) fa.pop_back();
    return fa;
}

void solve() {
    int n, m;
    cin >> n >> m;
    vector<Mod998> a(n), b(m);
    for (int i = 0; i < n; i++) cin >> a[i];
    for (int i = 0; i < m; i++) cin >> b[i];

    for (auto x : convolve998(a, b)) cout << x << " ";
    cout << endl;
}