「BZOJ 3438」小 M 的作物 - 最大权闭合图

小 M 有耕地 A B ,有 n 中作物的种子,第i种作物种植在 A 中种植可以获得 a_i 的收益,在 B 中种植可以获得 b_i 的收益。 共有 m 种作物组合,第 i 个组合中的作物共同种在 A 中可以获得 c_{1_i} 的额外收益,共同总在 B 中可以获得 c_{2_i} 的额外收益。 求最大收益。

链接

BZOJ 3438

题解

首先考虑将所有作物种植在 A 中,收益为 \sum\limits_{i = 1} ^ n a_i + \sum\limits_{i = 1} ^ m c_{1_i}

考虑选择一些作物改为种在 B 中,任意一个作物种在 B 中后,包含该作物的组合 i c_{1_i} 将失去。如果一个组合中所有作物都种在 B 中,则该组合 i c_{2_i} 将被获得。

建立最大权闭合图模型。每个作物的点权为 B_i - A_i ,每个组合拆为两个点,一个点表示损失掉的 c_{1_i} ,另一个点表示得到的 c_{2_i}

代码

#include <cstdio>
#include <climits>
#include <queue>
#include <algorithm>

const int MAXN = 1000;
const int MAXM = 1000;

struct Node;
struct Edge;

struct Node {
    Edge *e, *c;
    int l;
} N[MAXN + MAXM * 2 + 2];

struct Edge {
    Node *s, *t;
    int f, c;
    Edge *next, *r;

    Edge(Node *s, Node *t, const int c) : s(s), t(t), f(0), c(c), next(s->e) {}
};

struct Dinic {
    bool makeLevelGraph(Node *s, Node *t, const int n) {
        for (int i = 0; i < n; i++) N[i].l = 0, N[i].c = N[i].e;

        std::queue<Node *> q;
        q.push(s);
        s->l = 1;

        while (!q.empty()) {
            Node *v = q.front();
            q.pop();

            for (Edge *e = v->e; e; e = e->next) if (!e->t->l && e->f < e->c) {
                e->t->l = v->l + 1;
                if (e->t == t) return true;
                else q.push(e->t);
            }
        }

        return false;
    }

    int findPath(Node *s, Node *t, const int limit = INT_MAX) {
        if (s == t) return limit;

        for (Edge *&e = s->c; e; e = e->next) if (e->t->l == s->l + 1 && e->f < e->c) {
            int f = findPath(e->t, t, std::min(limit, e->c - e->f));
            if (f) {
                e->f += f, e->r->f -= f;
                return f;
            }
        }

        return 0;
    }

    int operator()(const int s, const int t, const int n) {
        int res = 0;
        while (makeLevelGraph(&N[s], &N[t], n)) {
            int f;
            while ((f = findPath(&N[s], &N[t])) > 0) res += f;
        }

        return res;
    }
} dinic;

inline void addEdge(const int s, const int t, const int c) {
    // printf("[%d, %d] = %d\n", s, t, c);
    N[s].e = new Edge(&N[s], &N[t], c);
    N[t].e = new Edge(&N[t], &N[s], 0);
    (N[s].e->r = N[t].e)->r = N[s].e;
}

int s, t;

int main() {
    int n;
    scanf("%d", &n);

    int sum = 0;

    static int a[MAXN], b[MAXN];
    for (int i = 0; i < n; i++) scanf("%d", &a[i]), sum += a[i];
    for (int i = 0; i < n; i++) scanf("%d", &b[i]);

    int m;
    scanf("%d", &m);

    const int s = 0, t = n + m * 2 + 1;
    for (int i = 1; i <= n; i++) {
        int w = b[i - 1] - a[i - 1];
        if (w > 0) addEdge(s, i, w), sum += w;
        else addEdge(i, t, -w);
    }

    for (int i = n + 1; i <= n + m; i++) {
        int k, c1, c2;
        scanf("%d %d %d", &k, &c1, &c2);

        addEdge(i, t, c1);
        addEdge(s, i + m, c2);

        sum += c1 + c2;

        for (int j = 0; j < k; j++) {
            int x;