CSP-Sで出題される可能性のあるテンプレート集(非原创、各所からまとめ)

CSP-Sの点数を上げるためのテンプレート集

数学

高速累乗

int pow_mod(int base, int exp) {
    int result = 1;
    while (exp > 0) {
        if (exp & 1) result = (result * base) % MOD;
        base = (base * base) % MOD;
        exp >>= 1;
    }
    return result;
}

ルーカスの定理(逆元の線形計算付き)

int inv[N], fact[N], fact_inv[N];
void prepare() {
    inv[1] = 1;
    for (int i = 2; i < N; i++) inv[i] = (MOD - MOD / i) * inv[MOD % i] % MOD;
    fact_inv[0] = fact_inv[1] = 1;
    for (int i = 2; i < N; i++) fact_inv[i] = fact_inv[i - 1] * inv[i] % MOD;
    fact[0] = fact[1] = 1;
    for (int i = 2; i < N; i++) fact[i] = fact[i - 1] * i % MOD;
}
int comb(int n, int r) {
    if (r > n) return 0;
    return fact[n] * fact_inv[n - r] % MOD * fact_inv[r] % MOD;
}
int lucas(int n, int r) {
    if (r == 0) return 1;
    return comb(n % MOD, r % MOD) * lucas(n / MOD, r / MOD) % MOD;
}

拡張ユークリッドアルゴリズム

int extended_gcd(int a, int b, int &x, int &y) {
    if (b == 0) {
        x = 1;
        y = 0;
        return a;
    }
    int gcd = extended_gcd(b, a % b, y, x);
    y -= (a / b) * x;
    return gcd;
}

拡張ユークリッドによる逆元計算

long long mod_inverse(long long a, long long mod) {
    long long x, y;
    long long gcd = extended_gcd(a, mod, x, y);
    return (gcd == 1) ? (x % mod + mod) % mod : -1;
}

フェルマの小定理による逆元

long long mod_inverse(long long a, long long mod) {
    return power(a, mod - 2, mod);
}

線形逆元の計算

for (int i = 2; i < n; i++) {
    inv[i] = inv[mod % i] * (mod - mod / i) % mod;
}

CRT(中国剰余定理)

long long chinese_remainder_theorem(int k, long long *a, long long *r) {
    long long product = 1, result = 0;
    for (int i = 1; i <= k; i++) product *= r[i];
    for (int i = 1; i <= k; i++) {
        long long m = product / r[i], b, y;
        extended_gcd(m, r[i], b, y);
        result = (result + a[i] * m * b % product) % product;
    }
    return (result % product + product) % product;
}

拡張CRT

long long extended_chinese_remainder() {
    long long mod = b[1];
    long long res = a[1];
    for (long long i = 2; i <= n; ++i) {
        long long diff = (a[i] - res % b[i] + b[i]) % b[i];
        long long g = extended_gcd(mod, b[i], x, y);
        long long p = b[i] / g;
        x = (x * diff / g % p + p) % p;
        res += x * mod;
        mod *= p;
        res = (res % mod + mod) % mod;
    }
    return res;
}

オイラーの篩

bool is_prime[N];
int primes[N], phi[N], count = 1;
void sieve() {
    phi[1] = 1;
    for (int i = 2; i < N; i++) {
        if (!is_prime[i]) {
            primes[count++] = i;
            phi[i] = i - 1;
        }
        for (int j = 1; j < count && i * primes[j] < N; j++) {
            is_prime[i * primes[j]] = true;
            if (i % primes[j] == 0) {
                phi[i * primes[j]] = phi[i] * primes[j];
                break;
            } else {
                phi[i * primes[j]] = phi[i] * phi[primes[j]];
            }
        }
    }
}

ミラーラビン素数判定

bool miller_rabin(long long n, int rounds) {
    if (n == 2) return true;
    if (n < 2 || (n & 1) == 0) return false;
    long long u = n - 1, x;
    int t = 0;
    while ((u & 1) == 0) {
        t++;
        u >>= 1;
    }
    for (int i = 0; i < rounds; i++) {
        long long a = rand() % (n - 1) + 1;
        x = power(a, u, n);
        for (int j = 0; j < t; j++) {
            long long y = (x * x) % n;
            if (y == 1 && x != 1 && x != n - 1) return false;
            x = y;
        }
        if (x != 1) return false;
    }
    return true;
}

オイラー関数(単体)

long long euler_phi(long long n) {
    long long result = n;
    for (long long i = 2; i * i <= n; i++) {
        if (n % i == 0) {
            result -= result / i;
            while (n % i == 0) n /= i;
        }
    }
    if (n > 1) result -= result / n;
    return result;
}

除算ブロック

for (int left = 1, right; left <= n; left = right + 1) {
    right = n / (n / left);
    cout << (n / left) << ' ' << (right - left + 1) << endl;
}

スキャンライン

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e6 + 10;
typedef long long ll;

int n, cnt = 0;
ll x1, y1, x2, y2, X[MAXN << 1];

struct ScanLine {
    ll l, r, h;
    int mark;
    bool operator < (const ScanLine &rhs) const {
        return h < rhs.h;
    }
} lines[MAXN << 1];

struct SegmentTree {
    int l, r, sum;
    ll len;
} tree[MAXN << 2];

void build_tree(int x, int l, int r) {
    tree[x].l = l;
    tree[x].r = r;
    tree[x].len = 0;
    tree[x].sum = 0;
    if (l == r) return;
    int mid = (l + r) >> 1;
    build_tree(x << 1, l, mid);
    build_tree(x << 1 | 1, mid + 1, r);
}

void pushup(int x) {
    if (tree[x].sum) {
        tree[x].len = X[tree[x].r + 1] - X[tree[x].l];
    } else {
        tree[x].len = tree[x << 1].len + tree[x << 1 | 1].len;
    }
}

void update_tree(int x, ll L, ll R, int c) {
    int l = tree[x].l, r = tree[x].r;
    if (X[r + 1] <= L || R <= X[l]) return;
    if (L <= X[l] && X[r + 1] <= R) {
        tree[x].sum += c;
        pushup(x);
        return;
    }
    update_tree(x << 1, L, R, c);
    update_tree(x << 1 | 1, L, R, c);
    pushup(x);
}

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%lli %lli %lli %lli", &x1, &y1, &x2, &y2);
        X[2 * i - 1] = x1;
        X[2 * i] = x2;
        lines[2 * i - 1] = (ScanLine) {x1, x2, y1, 1};
        lines[2 * i] = (ScanLine) {x1, x2, y2, -1};
    }
    n <<= 1;
    sort(lines + 1, lines + n + 1);
    sort(X + 1, X + n + 1);
    int total = unique(X + 1, X + n + 1) - X - 1;
    build_tree(1, 1, total - 1);
    ll area = 0;
    for (int i = 1; i < n; i++) {
        update_tree(1, lines[i].l, lines[i].r, lines[i].mark);
        area += tree[1].len * (lines[i + 1].h - lines[i].h);
    }
    printf("%lli", area);
    return 0;
}

ガウス消去法

#include <bits/stdc++.h>
using namespace std;
const int N = 105;
int n;
double matrix[N][N], epsilon = 1e-6;

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n + 1; j++) {
            cin >> matrix[i][j];
        }
    }
    int current_row = 1;
    for (int col = 1; col <= n; col++) {
        int pivot_row = current_row;
        for (int row = current_row; row <= n; row++) {
            if (abs(matrix[row][col]) > abs(matrix[pivot_row][col])) {
                pivot_row = row;
            }
        }
        if (abs(matrix[pivot_row][col]) < epsilon) continue;
        swap(matrix[current_row], matrix[pivot_row]);
        for (int j = n + 1; j >= col; j--) {
            matrix[current_row][j] /= matrix[current_row][col];
        }
        for (int row = current_row + 1; row <= n; row++) {
            for (int j = n + 1; j >= col; j--) {
                matrix[row][j] -= matrix[current_row][j] * matrix[row][col];
            }
        }
        current_row++;
    }
    if (current_row <= n) {
        for (int i = current_row; i <= n; i++) {
            if (abs(matrix[i][n + 1]) > epsilon) {
                cout << -1;
                return 0;
            }
        }
        cout << 0;
        return 0;
    }
    for (int i = n; i >= 1; i--) {
        for (int j = i + 1; j <= n; j++) {
            matrix[i][n + 1] -= matrix[i][j] * matrix[j][n + 1];
        }
    }
    for (int i = 1; i <= n; i++) {
        cout << "x" << i << "=" << matrix[i][n + 1] << '\n';
    }
}

データ構造

単調スタック

void push(int value) {
    while (!stack.empty() && stack.top() < value) stack.pop();
    stack.push(value);
}

単調キュー

deque<int> dq;
for (int i = 1; i <= n; i++) {
    int input;
    cin >> input;
    while (!dq.empty() && dq.back() > input) dq.pop_back();
    dq.push_back(input);
}

01トライ

int trie[N * 31][2], node_count = 1;
void insert(int pos, int depth, int value) {
    bool bit = (value & (1 << depth));
    if (!trie[pos][bit]) trie[pos][bit] = ++node_count;
    if (depth) insert(trie[pos][bit], depth - 1, value);
}
int query(int pos, int depth, int value) {
    if (depth < 0) return 0;
    bool bit = (value & (1 << depth));
    if (trie[pos][!bit]) return (1 << depth) + query(trie[pos][!bit], depth - 1, value);
    if (trie[pos][bit]) return query(trie[pos][bit], depth - 1, value);
    return 0;
}

ST表

#include <bits/stdc++.h>
using namespace std;
const int LOGN = 21;
const int MAXN = 2000001;
int st_table[MAXN][LOGN + 1], log_values[MAXN + 1];

int read() {
    char c = getchar();
    int x = 0, f = 1;
    while (c < '0' || c > '9') {
        if (c == '-') f = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9') {
        x = x * 10 + c - '0';
        c = getchar();
    }
    return x * f;
}

void preprocess() {
    log_values[1] = 0;
    log_values[2] = 1;
    for (int i = 3; i < MAXN; i++) {
        log_values[i] = log_values[i / 2] + 1;
    }
}

int main() {
    int n = read(), m = read();
    for (int i = 1; i <= n; i++) st_table[i][0] = read();
    preprocess();
    for (int j = 1; j <= LOGN; j++) {
        for (int i = 1; i + (1 << j) - 1 <= n; i++) {
            st_table[i][j] = max(st_table[i][j - 1], st_table[i + (1 << (j - 1))][j - 1]);
        }
    }
    for (int i = 1; i <= m; i++) {
        int x = read(), y = read();
        int s = log_values[y - x + 1];
        printf("%d\n", max(st_table[x][s], st_table[y - (1 << s) + 1][s]));
    }
    return 0;
}

フィックステータス木

#define low_bit(x) ((x) & -(x))
using namespace std;
int n, m, tree[200000 + 10];
void update(int index, int delta) {
    for (int i = index; i <= n; i += low_bit(i)) tree[i] += delta;
}
int query(int range) {
    int result = 0;
    for (int i = range; i >= 1; i -= low_bit(i)) result += tree[i];
    return result;
}

線分木

void add_tag(int node, int value) {
    seg[node].tag += value;
    seg[node].sum += (seg[node].r - seg[node].l + 1) * value;
}
void push_up(int node) {
    seg[node].sum = seg[left_child(node)].sum + seg[right_child(node)].sum;
}
void push_down(int node) {
    add_tag(left_child(node), seg[node].tag);
    add_tag(right_child(node), seg[node].tag);
    seg[node].tag = 0;
}
void build(int node, int left, int right) {
    seg[node].l = left;
    seg[node].r = right;
    if (left == right) {
        seg[node].sum = arr[left];
        seg[node].tag = 0;
        return;
    }
    int mid = left + (right - left) / 2;
    build(left_child(node), left, mid);
    build(right_child(node), mid + 1, right);
    push_up(node);
}
void modify(int node, int left, int right, int value) {
    if (left <= seg[node].l && seg[node].r <= right) {
        add_tag(node, value);
        return;
    }
    push_down(node);
    if (left <= seg[left_child(node)].r) modify(left_child(node), left, right, value);
    if (seg[right_child(node)].l <= right) modify(right_child(node), left, right, value);
    push_up(node);
}
int query(int node, int left, int right) {
    if (left <= seg[node].l && seg[node].r <= right) return seg[node].sum;
    push_down(node);
    int sum = 0;
    if (left <= seg[left_child(node)].r) sum += query(left_child(node), left, right);
    if (seg[right_child(node)].l <= right) sum += query(right_child(node), left, right);
    return sum;
}

FHQ Treap

mt19937 rng(time(0));
const int MAX_N = 1e6 + 10;
class Node {
public:
    int lson = 0, rson = 0;
    int value, priority, size = 0;
    Node() {
        lson = rson = size = 0;
    }
};
class Treap {
public:
    int size, root;
    Node nodes[MAX_N];
    int new_node(int val) {
        nodes[++size].value = val;
        nodes[size].priority = rng();
        nodes[size].lson = nodes[size].rson = 0;
        nodes[size].size = 1;
        return size;
    }
    Treap() {
        size = 0;
        root = 0;
    }
    void push_up(int node) {
        nodes[node].size = nodes[nodes[node].lson].size + nodes[nodes[node].rson].size + 1;
    }
    void split(int node, int key, int &left, int &right) {
        if (!node) {
            left = right = 0;
            return;
        } else if (nodes[node].value <= key) {
            left = node;
            split(nodes[node].rson, key, nodes[node].rson, right);
        } else {
            right = node;
            split(nodes[node].lson, key, left, nodes[node].lson);
        }
        push_up(node);
    }
    int merge(int left, int right) {
        if (!left || !right) return left + right;
        if (nodes[left].priority > nodes[right].priority) {
            nodes[left].rson = merge(nodes[left].rson, right);
            push_up(left);
            return left;
        } else {
            nodes[right].lson = merge(left, nodes[right].lson);
            push_up(right);
            return right;
        }
    }
    void insert(int val) {
        int left, right;
        split(root, val, left, right);
        root = merge(merge(left, new_node(val)), right);
    }
    void remove(int val) {
        int left, middle, right;
        split(root, val, left, right);
        split(left, val - 1, left, middle);
        middle = merge(nodes[middle].lson, nodes[middle].rson);
        root = merge(merge(left, middle), right);
    }
    int rank(int val) {
        int left, right;
        split(root, val - 1, left, right);
        int result = nodes[left].size;
        root = merge(left, right);
        return result + 1;
    }
    int get_value_by_rank(int rnk) {
        int current = root;
        while (current) {
            if (nodes[nodes[current].lson].size + 1 == rnk) break;
            else if (nodes[nodes[current].lson].size + 1 > rnk)
                current = nodes[current].lson;
            else {
                rnk -= nodes[nodes[current].lson].size + 1;
                current = nodes[current].rson;
            }
        }
        return nodes[current].value;
    }
    int predecessor(int val) {
        int left, right;
        split(root, val - 1, left, right);
        int current = left;
        while (nodes[current].rson) current = nodes[current].rson;
        int result = nodes[current].value;
        root = merge(left, right);
        return result;
    }
    int successor(int val) {
        int left, right;
        split(root, val, left, right);
        int current = right;
        while (nodes[current].lson) current = nodes[current].lson;
        int result = nodes[current].value;
        root = merge(left, right);
        return result;
    }
};

除算ブロック

for (int left = 1, right; left <= n; left = right + 1) {
    right = n / (n / left);
    cout << (n / left) << ' ' << (right - left + 1) << endl;
}

文字列

AC自動機(/KMP)

struct TrieNode {
    int children[26], fail, flag, count;
    void clear() {
        memset(children, 0, sizeof(children));
        fail = flag = count = 0;
    }
} trie[MAXN];
queue<int> q;

void insert(char* str, int id) {
    int current = 1, length = strlen(str);
    for (int i = 0; i < length; i++) {
        int ch = str[i] - 'a';
        if (!trie[current].children[ch]) trie[current].children[ch] = ++node_count;
        current = trie[current].children[ch];
    }
    if (!trie[current].flag) trie[current].flag = id;
    mapping[id] = trie[current].flag;
}

void compute_fail() {
    for (int i = 0; i < 26; i++) trie[0].children[i] = 1;
    q.push(1);
    while (!q.empty()) {
        int current = q.front();
        q.pop();
        int failure = trie[current].fail;
        for (int i = 0; i < 26; i++) {
            int child = trie[current].children[i];
            if (!child) {
                trie[current].children[i] = trie[failure].children[i];
                continue;
            }
            trie[child].fail = trie[failure].children[i];
            in[trie[child].fail]++;
            q.push(child);
        }
    }
}

void topological_sort() {
    for (int i = 1; i <= node_count; i++) {
        if (in[i] == 0) q.push(i);
    }
    while (!q.empty()) {
        int current = q.front();
        q.pop();
        visited[trie[current].flag] = trie[current].count;
        int parent = trie[current].fail;
        in[parent]--;
        trie[parent].count += trie[current].count;
        if (in[parent] == 0) q.push(parent);
    }
}

void search(char* text) {
    int current = 1, length = strlen(text);
    for (int i = 0; i < length; i++) {
        current = trie[current].children[text[i] - 'a'];
        trie[current].count++;
    }
}

文字列ハッシュ

typedef unsigned long long ull;
const ull SEED_POOL[] = {146527, 19260817};
const ull MOD_POOL[] = {1000000009, 998244353};

struct Hash {
    ull seed, mod;
    vector<ull> power, prefix_sum;
    
    Hash() {}
    
    Hash(const string& str, const int seed_idx, const int mod_idx) {
        seed = SEED_POOL[seed_idx];
        mod = MOD_POOL[mod_idx];
        int len = str.length();
        power.resize(len + 1);
        prefix_sum.resize(len + 1);
        power[0] = 1;
        for (int i = 1; i <= len; i++) power[i] = power[i - 1] * seed % mod;
        for (int i = 1; i <= len; i++) prefix_sum[i] = (prefix_sum[i - 1] * seed % mod + str[i - 1]) % mod;
    }
    
    ull get(int start, int end) {
        return (prefix_sum[end] - prefix_sum[start] * power[end - start] % mod + mod) % mod;
    }
    
    ull substring(int start, int length) {
        return get(start, start + length);
    }
};

グラフ理論

隣接リスト表現

struct Edge {
    int to, weight, next;
} edges[M * 2];
int head[N], edge_count;

void add_edge(int from, int to, int weight) {
    edges[++edge_count] = (Edge){to, weight, head[from]};
    head[from] = edge_count;
}

Dijkstra

struct Edge {
    int to, weight;
};
vector<Edge> graph[N];
struct Node {
    int vertex, distance;
    bool operator<(const Node& other) const {
        return distance > other.distance;
    }
};
priority_queue<Node> pq;
int distances[N];
bool visited[N];

void dijkstra(int start) {
    memset(distances, 0x3f, sizeof distances);
    pq.push({start, 0});
    distances[start] = 0;
    while (!pq.empty()) {
        int current = pq.top().vertex;
        pq.pop();
        if (visited[current]) continue;
        visited[current] = true;
        for (int i = 0; i < graph[current].size(); i++) {
            int neighbor = graph[current][i].to;
            int cost = distances[current] + graph[current][i].weight;
            if (distances[neighbor] > cost) {
                distances[neighbor] = cost;
                pq.push({neighbor, cost});
            }
        }
    }
}

木の分割(重み付き)

void dfs(int node, int parent) {
    subtree_size[node] = 1;
    for (int &child : children[node]) {
        if (child != parent) {
            parent_of[child] = node;
            depth[child] = depth[node] + 1;
            dfs(child, node);
            subtree_size[node] += subtree_size[child];
            if (subtree_size[child] >= subtree_size[heavy_child[node]])
                heavy_child[node] = child, swap(child, children[node][0]);
        }
    }
}

void dfs2(int node, int parent, int chain_head) {
    dfn[node] = ++counter;
    position[dfn[node]] = node;
    chain_head[node] = chain_head;
    for (int &child : children[node]) {
        if (child != parent) {
            if (child == heavy_child[node])
                dfs2(child, node, chain_head);
            else
                dfs2(child, node, child);
        }
    }
}

int lca(int a, int b) {
    while (chain_head[a] != chain_head[b]) {
        if (depth[chain_head[b]] > depth[chain_head[a]])
            swap(a, b);
        a = parent_of[chain_head[a]];
    }
    return depth[a] > depth[b] ? b : a;
}

倍増LCA

void dfs(int root, int parent) {
    ancestor[root][0] = parent;
    depth[root] = depth[parent] + 1;
    for (int i = 1; i < 31; ++i) {
        ancestor[root][i] = ancestor[ancestor[root][i - 1]][i - 1];
        cost[root][i] = cost[ancestor[root][i - 1]][i - 1] + cost[root][i - 1];
    }
    for (auto child : children[root]) {
        if (child == parent) continue;
        cost[child][0] = weight[root][child];
        dfs(child, root);
    }
}

int find_lca(int a, int b) {
    if (depth[a] > depth[b]) swap(a, b);
    int diff = depth[b] - depth[a], total_cost = 0;
    for (int j = 0; diff; ++j, diff >>= 1)
        if (diff & 1) total_cost += cost[b][j], b = ancestor[b][j];
    if (b == a) return total_cost;
    for (int j = 30; j >= 0 && b != a; --j) {
        if (ancestor[a][j] != ancestor[b][j]) {
            total_cost += cost[a][j] + cost[b][j];
            a = ancestor[a][j];
            b = ancestor[b][j];
        }
    }
    total_cost += cost[a][0] + cost[b][0];
    return total_cost;
}

強連結成分分解(Tarjan)

void tarjan(int node) {
    dfn[node] = low[node] = ++counter;
    visited[node] = true;
    stack.push(node);
    for (auto neighbor : graph[node]) {
        if (!dfn[neighbor]) {
            tarjan(neighbor);
            low[node] = min(low[node], low[neighbor]);
        } else if (visited[neighbor]) {
            low[node] = min(low[node], dfn[neighbor]);
        }
    }
    if (low[node] == dfn[node]) {
        int current_node;
        do {
            current_node = stack.top();
            visited[current_node] = false;
            component[current_node] = component_count;
            components[component_count].push_back(current_node);
            stack.pop();
        } while (current_node != node);
        component_count++;
    }
}

トポロジカルソート

bool topological_sort() {
    vector<int> result;
    queue<int> zero_in_degree;
    for (int i = 1; i <= n; i++) {
        if (in_degree[i] == 0) zero_in_degree.push(i);
    }
    while (!zero_in_degree.empty()) {
        int current = zero_in_degree.front();
        zero_in_degree.pop();
        result.push_back(current);
        for (auto neighbor : graph[current]) {
            if (--in_degree[neighbor] == 0) {
                zero_in_degree.push(neighbor);
            }
        }
    }
    if (result.size() == n) {
        for (auto i : result) cout << i << ' ';
        return true;
    } else {
        return false;
    }
}

最小生成木(Kruskal)

for (int i = 1; i <= n; i++) parent[i] = i;
while (!edges.empty()) {
    auto [weight, u, v] = edges.top();
    edges.pop();
    if (find_parent(u) == find_parent(v)) continue;
    if (!(n--) || !(k--)) break;
    union_sets(u, v);
    max_weight = max(max_weight, weight);
}

Dinic最大流

int n, m, source, sink, level[N], head[N], current[N];
struct Edge {
    int to, capacity, next;
} edges[M * 2];
int edge_count = 1;

void add_edge(int from, int to, int capacity) {
    edges[++edge_count] = (Edge){to, capacity, head[from]};
    head[from] = edge_count;
}

void link_edge(int from, int to, int capacity) {
    add_edge(from, to, capacity);
    add_edge(to, from, 0);
}

bool bfs() {
    queue<int> q;
    for (int i = source; i <= sink; i++) level[i] = -1;
    level[source] = 0;
    q.push(source);
    while (!q.empty()) {
        int current = q.front();
        current_flow[current] = head[current];
        q.pop();
        for (int i = head[current]; i; i = edges[i].next) {
            if (level[edges[i].to] == -1 && edges[i].capacity) {
                level[edges[i].to] = level[current] + 1;
                q.push(edges[i].to);
            }
        }
    }
    return level[sink] != -1;
}

int dfs(int node, int flow) {
    if (node == sink) return flow;
    int total_flow = 0;
    for (int i = current[node]; i && flow; i = edges[i].next) {
        current[node] = i;
        if (level[edges[i].to] == level[node] + 1 && edges[i].capacity) {
            int result = dfs(edges[i].to, min(flow, edges[i].capacity));
            if (result > 0) {
                edges[i].capacity -= result;
                edges[i ^ 1].capacity += result;
                total_flow += result;
                flow -= result;
            }
        }
    }
    return total_flow;
}

最小費用流(MCMF)

int n, m, source, sink, dist[N], head[N], current[N], total_cost, total_flow;
bool visited[N];
struct Edge {
    int to, capacity, cost, next;
} edges[M * 2];
int edge_count = 1;

void add_edge(int from, int to, int capacity, int cost) {
    edges[++edge_count] = (Edge){to, capacity, cost, head[from]};
    head[from] = edge_count;
}

void link_edge(int from, int to, int capacity, int cost) {
    add_edge(from, to, capacity, cost);
    add_edge(to, from, 0, -cost);
}

bool spfa() {
    for (int i = source; i <= sink; i++) dist[i] = 1e9;
    for (int i = source; i <= sink; i++) visited[i] = false;
    queue<int> q;
    q.push(source);
    visited[source] = true;
    dist[source] = 0;
    while (!q.empty()) {
        int current = q.front();
        current_flow[current] = head[current];
        q.pop();
        visited[current] = false;
        for (int i = head[current]; i; i = edges[i].next) {
            if (dist[edges[i].to] > dist[current] + edges[i].cost && edges[i].capacity) {
                dist[edges[i].to] = dist[current] + edges[i].cost;
                if (!visited[edges[i].to]) {
                    q.push(edges[i].to);
                    visited[edges[i].to] = true;
                }
            }
        }
    }
    return dist[sink] != 1e9;
}

int dfs(int node, int flow) {
    if (node == sink) return flow;
    visited[node] = true;
    int total_flow = 0;
    for (int i = current[node]; i && flow; i = edges[i].next) {
        current[node] = i;
        if (!visited[edges[i].to] && dist[edges[i].to] == dist[node] + edges[i].cost && edges[i].capacity) {
            int result = dfs(edges[i].to, min(flow, edges[i].capacity));
            edges[i].capacity -= result;
            edges[i ^ 1].capacity += result;
            total_flow += result;
            flow -= result;
            total_cost += result * edges[i].cost;
        }
    }
    visited[node] = false;
    return total_flow;
}

その他の手法

行列の高速累乗

typedef vector<long long> vec;
typedef vector<vec> mat;

mat multiply(mat& A, mat& B) {
    mat C(A.size(), vec(B[0].size()));
    for (int i = 0; i < A.size(); i++)
        for (int k = 0; k < B.size(); k++)
            if (A[i][k])
                for (int j = 0; j < B[0].size(); j++)
                    C[i][j] = (C[i][j] + A[i][k] * B[k][j]) % MOD;
    return C;
}

mat power(mat A, long long n) {
    mat result(A.size(), vec(A.size()));
    for (int i = 0; i < A.size(); i++) result[i][i] = 1;
    for (; n; n >>= 1, A = multiply(A, A))
        if (n & 1) result = multiply(result, A);
    return result;
}

タグ: CSP-S テンプレート 数学 データ構造 文字列

5月22日 10:39 投稿