CF1045A Last chance 题解

一、题目:

codeforces原题

洛谷原题

二、思路:

很明显这是一道网络流的题目。

首先考虑朴素做法。每个武器对应着一个点,每艘宇宙飞船对应着一个点。对于第一、二种武器SQL rockets和Cognition Beams,我们从该点向它能打到的飞船连边(容量为1)。别忘了从源点向这些武器连容量也为1的边。

对于第三种武器OMG bazooka,我们首先从源点向该点连一条容量为2的边,然后从该点向他能打到的三个点分别连边(容量同样是1)。

最后从每艘宇宙飞船向汇点连容量为1的边。

然后呢?然后发现空间炸上天去了。

所以显然能想到线段树优化建图,把第二种武器的边给优化掉。注意线段树上的边的容量都是正无穷。

考虑怎么输出方案。我们跑完Dinic之后,在残量网络容量大于零反向边上退流一遍即可。这个其实很简单,看代码理解就可以了。

这样就做完了吗?还没有。

注意到题目中的限制,第三种武器OMG bazooka只能打掉0艘或2艘的宇宙飞船。而如果我们仅仅按上述方法连边的话,会导致可能会有打1艘宇宙飞船的情况。

但是题目中还有一个限制:

In addition, due to the smart targeting system, the sets of the three possible targets of any two different OMG bazookas are disjoint (that means that every ship is targeted with at most one OMG bazooka).

也就是说第三种武器的目标两两不相交。

所以,一旦发生第三种武器只匹配到一艘宇宙飞船的情况,说明剩下两艘宇宙飞船都是被第一种或者第二种武器匹配上了。所以我们随意将剩下两艘宇宙飞船中的任意一艘强制匹配到该武器上即可。

三、代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>

using namespace std;

#define mem(s, v) memset(s, v, sizeof s)

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

const int maxn = 5005, inf = 0x3f3f3f3f, maxv = maxn + maxn * 4, maxk = 1e5 + 5;
const int maxe = maxn + maxk + maxn * 14 + 4 * maxn * 2 + maxn;

int n, m, head[maxv], tot = 1, S, T;
int cur[maxv], d[maxv], id[maxn << 2], ans[maxn];
bool is_second[maxn];

int spe[maxn][3], cnt[maxn];

struct Edge {
    int y, next, w;
    Edge() {}
    Edge(int _y, int _next, int _w) : y(_y), next(_next), w(_w) {}
}e[maxe << 1];

inline void connect(int x, int y, int w) {
    e[++tot] = Edge(y, head[x], w);
    head[x] = tot;
}

inline int get(int x) {
    return x + 4 * m;
}

struct SegmentTree {
#define lson (o << 1)
#define rson (o << 1 | 1)
    int L[maxn << 2], R[maxn << 2];
    void build(int o, int l, int r) {
        L[o] = l; R[o] = r;
        if (l == r) {
            connect(o, T, 1); connect(T, o, 0);
            id[o] = l;
            return;
        }
        int mid = (l + r) >> 1;
        build(lson, l, mid); build(rson, mid + 1, r);
        connect(o, lson, inf); connect(lson, o, 0);
        connect(o, rson, inf); connect(rson, o, 0);
    }
    void add(int o, int ql, int qr, int fr) {
        if (ql <= L[o] && R[o] <= qr) { connect(fr, o, 1); connect(o, fr, 0); return; }
        int mid = (L[o] + R[o]) >> 1;
        if (ql <= mid) add(lson, ql, qr, fr);
        if (qr > mid) add(rson, ql, qr, fr);
    }
    int find(int o, int q) {
        if (L[o] == R[o]) { return o; }
        int mid = (L[o] + R[o]) >> 1;
        if (q <= mid) return find(lson, q);
        else return find(rson, q);
    }
}Tree;

inline bool bfs(void) {
    queue<int>q;
    q.push(S);
    mem(d, -1); d[S] = 1; cur[S] = head[S];
    while (q.size()) {
        int x = q.front(); q.pop();
        for (int i = head[x]; i; i = e[i].next) {
            int y = e[i].y;
            if (d[y] == -1 && e[i].w) {
                d[y] = d[x] + 1;
                cur[y] = head[y];
                if (y == T) return true;
                q.push(y);
            }
        }
    }
    return false;
}

int find(int x, int limit) {
    if (x == T) return limit;
    int flow = 0;
    for (int i = cur[x]; i && flow < limit; i = e[i].next) {
        int y = e[i].y;
        cur[x] = i;
        if (d[y] == d[x] + 1 && e[i].w) {
            int t = find(y, min(e[i].w, limit - flow));
            if (!t) d[y] = -1;
            e[i].w -= t; e[i ^ 1].w += t; flow += t;
        }
    }
    return flow;
}

inline int dinic(void) {
    int r = 0, flow;
    while (bfs()) while ((flow = find(S, inf)) > 0) r += flow;
    return r;
}

void scheme(int now, int& s) { // 退流
    if (s) return;
    if (4 * m + 1 <= now && now <= 4 * m + n) {
        s = now - 4 * m;
        return;
    }
    for (int i = head[now]; i; i = e[i].next) {
        int y = e[i].y;
        if ((i & 1) && e[i].w) {
            scheme(y, s);
            --e[i].w;
            return; // 注意及时退出,否则会导致某些边的容量被减多次
        }
    }
}

int main() {
    n = read(); m = read();
    S = 0; T = n + 4 * m + 1;
    Tree.build(1, 1, m);
    for (int i = 1; i <= n; ++i) {
        int opt = read();
        if (opt == 0) {
            int K = read();
            while (K--) {
                int x = read();
                connect(get(i), Tree.find(1, x), 1);
                connect(Tree.find(1, x), get(i), 0);
            }
            connect(S, get(i), 1); connect(get(i), S, 0);
        }
        else if (opt == 1) {
            int l = read(), r = read();
            Tree.add(1, l, r, get(i));
            connect(S, get(i), 1); connect(get(i), S, 0);
        }
        else if (opt == 2) {
            int a = read(), b = read(), c = read();
            connect(get(i), Tree.find(1, a), 1); connect(Tree.find(1, a), get(i), 0);
            connect(get(i), Tree.find(1, b), 1); connect(Tree.find(1, b), get(i), 0);
            connect(get(i), Tree.find(1, c), 1); connect(Tree.find(1, c), get(i), 0);
            connect(S, get(i), 2); connect(get(i), S, 0);
            is_second[i] = true;
            spe[i][0] = a;
            spe[i][1] = b;
            spe[i][2] = c;
        }
    }
    int tmp = dinic();
    printf("%d\n", tmp);
    for (int i = head[T]; i; i = e[i].next) {
        int y = e[i].y;
        if (e[i].w) {
            scheme(y, ans[id[y]]);
        }
    }
    for (int i = 1; i <= m; ++ i) {
        ++ cnt[ans[i]];
    }
    for (int i = 1; i <= n; ++ i) { // 检查是否存在不合法的第三种武器
        if (cnt[i] == 1 && is_second[i]) {
            if (ans[spe[i][0]] != i) ans[spe[i][0]] = i;
            else ans[spe[i][1]] = i;
        }
    }
    for (int i = 1; i <= m; ++ i) {
        if (ans[i]) printf("%d %d\n", ans[i], i);
    }
    return 0;
}

上一篇:【Gym】101194J Mr.Panda and TubeMaster(二分图费用流)


下一篇:HDU - 3277: Marriage Match III 网络流拆点 + 二分 + 并查集