Solution -「AGC 031E」Snuke the Phantom Thief


\(\mathscr{Description}\)

??Link.

??在一个网格图内有 \(n\) 个格子有正价值,给出四种限制:横 / 纵坐标不大于 / 不小于 \(a\) 的格子不能选超过 \(b\) 个。求能选出格子价值之和的最大值。

??\(n\le80\),坐标范围 \(1\le X\le100\),限制数量 \(m\le320\)

\(\mathscr{Solution}\)

??你看出来是网络流,但应当留意,图本身也许不是全部。

??大概感知一下,感觉是一个源点-横坐标-价值点-纵坐标-汇点的模型,但很难同时处理坐标上的前缀和限制与后缀和限制。怎么办?枚举最终选出的格子数 \(k\),它给我们带来了额外条件——对后缀的限制可以化归为对前缀的限制。

??具体地,以横坐标为例,先人为限制按横坐标升序选格子。对于 \(b 的某个限制,若限制前缀不超过 \(b\) 个,相当于选出的第 \(b+1..k\) 个格子的横坐标都得 \(>a\);若限制后缀不超过 \(b\) 个,相当于选出的第 \(1..k-b\) 个格子的横坐标都得 \(。纵坐标同理,规定纵坐标升序选即可,二者只是考虑角度不同,并不矛盾。依此跑费用流,若满流,用最大费用更新答案即可。

??复杂度 \(\mathcal O(n\operatorname{Dinic}(n,n^2))\),当然边数可以优化至 \(\mathcal O(n\log n)\)

\(\mathscr{Code}\)

/*+Rainybunny+*/

#include 

#define rep(i, l, r) for (int i = l, rep##i = r; i <= rep##i; ++i)
#define per(i, r, l) for (int i = r, per##i = l; i >= per##i; --i)

typedef long long LL;
typedef std::pair PIL;
#define fi first
#define se second

template 
inline void chkmin(Tp& u, const Tp& v) { v < u && (u = v, 0); }
template 
inline void chkmax(Tp& u, const Tp& v) { u < v && (u = v, 0); }
template 
inline Tp imin(const Tp& u, const Tp& v) { return u < v ? u : v; }
template 
inline Tp imax(const Tp& u, const Tp& v) { return u < v ? v : u; }

const int MAXN = 80, MAXM = 320, MAXX = 100, IINF = 0x3f3f3f3f;
const LL LINF = 1ll << 60;

namespace FG {

const int MAXND = MAXN * 2 + MAXX * 2 + 2;
const int MAXEG = MAXN * MAXN * 2 + MAXN * 3;
int S, T, ecnt = 1, head[MAXND + 5], curh[MAXND + 5];
LL dis[MAXND + 5];
struct Edge { int to, flw; LL cst; int nxt; } graph[MAXEG * 2 + 5];

inline void clear() {
    ecnt = 1;
    rep (i, S, T) head[i] = 0;
}

inline void link(const int s, const int t, const int f, const LL c) {
    // printf("%d %d %d,%lld\n", s, t, f, c);
    graph[++ecnt] = { t, f, c, head[s] }, head[s] = ecnt;
    graph[++ecnt] = { s, 0, -c, head[t] }, head[t] = ecnt;
}

inline bool spfa() {
    static std::queue que;
    static bool inq[MAXND + 5];
    rep (i, S, T) dis[i] = LINF;
    dis[S] = 0, que.push(S);
    while (!que.empty()) {
        int u = que.front(); que.pop(), inq[u] = false;
        for (int i = head[u], v; i; i = graph[i].nxt) {
            if (graph[i].flw && dis[v = graph[i].to] > dis[u] + graph[i].cst) {
                dis[v] = dis[u] + graph[i].cst;
                if (!inq[v]) que.push(v), inq[v] = true;
            }
        }
    }
    return dis[T] != LINF;
}

inline PIL augment(const int u, int iflw) {
    if (u == T) return { iflw, 0 };
    static bool instk[MAXND + 5]; instk[u] = true;
    PIL ret(0, 0);
    for (int &i = curh[u], v; i; i = graph[i].nxt) {
        if (graph[i].flw && !instk[v = graph[i].to]
          && dis[v] == dis[u] + graph[i].cst) {
            PIL t(augment(v, std::min(iflw, graph[i].flw)));
            ret.fi += t.fi, ret.se += t.se + t.fi * graph[i].cst;
            graph[i].flw -= t.fi, graph[i ^ 1].flw += t.fi, iflw -= t.fi;
            if (!iflw) break;
        }
    }
    if (!ret.fi) dis[u] = LINF;
    return instk[u] = false, ret;
}

inline PIL dinic() {
    PIL ret(0, 0);
    while (spfa()) {
        rep (i, S, T) curh[i] = head[i];
        PIL t(augment(S, IINF));
        ret.fi += t.fi, ret.se += t.se;
    }
    return ret;
}

} // namespace FG.

int n, m, jx[MAXN + 5], jy[MAXN + 5];
int lefx[MAXN + 5], rigx[MAXN + 5], lefy[MAXN + 5], rigy[MAXN + 5];
LL jv[MAXN + 5];
struct Restrict { int op, a, b; } lim[MAXM + 5];

int main() {
    scanf("%d", &n);
    rep (i, 1, n) scanf("%d %d %lld", &jx[i], &jy[i], &jv[i]);
    scanf("%d", &m);
    rep (i, 1, m) {
        char op[5];
        scanf("%s %d %d", op, &lim[i].a, &lim[i].b), lim[i].op = op[0];
    }

    LL ans = 0;
    rep (x, 1, n) {
        // x = 4; // debug.
        FG::clear();
        FG::S = 0, FG::T = 2 * x + 2 * n + 1;

        rep (i, 1, x) {
            FG::link(FG::S, i, 1, 0);
            FG::link(i + x, FG::T, 1, 0);
        }
        rep (i, 1, n) FG::link(i + 2 * x, i + n + 2 * x, 1, -jv[i]);

        rep (i, 1, x) lefx[i] = lefy[i] = 1, rigx[i] = rigy[i] = MAXX;
        rep (i, 1, m) if (lim[i].b < x) {
            if (lim[i].op == 'L') chkmax(lefx[lim[i].b + 1], lim[i].a + 1);
            if (lim[i].op == 'R') chkmin(rigx[x - lim[i].b], lim[i].a - 1);
            if (lim[i].op == 'U') chkmin(rigy[x - lim[i].b], lim[i].a - 1);
            if (lim[i].op == 'D') chkmax(lefy[lim[i].b + 1], lim[i].a + 1);
        }
        rep (i, 2, x) {
            chkmax(lefx[i], lefx[i - 1]), chkmax(lefy[i], lefy[i - 1]);
        }
        per (i, x - 1, 1) {
            chkmin(rigx[i], rigx[i + 1]), chkmin(rigy[i], rigy[i + 1]);
        }

        rep (i, 1, x) {
            rep (j, 1, n) {
                if (lefx[i] <= jx[j] && jx[j] <= rigx[i]) {
                    FG::link(i, j + 2 * x, 1, 0);
                }
                if (lefy[i] <= jy[j] && jy[j] <= rigy[i]) {
                    FG::link(j + n + 2 * x, i + x, 1, 0);
                }
            }
        }

        PIL res(FG::dinic());
        if (res.fi == x) chkmax(ans, -res.se);
        // break; // debug
    }
    printf("%lld\n", ans);
    return 0;
}

相关