P4701 粘骨牌

· · 题解

思路:

首先黑白染色,然后你会发现空的格子一定是黑子。

考虑一下移动的限制,因为一个 1 \times 2 骨牌一定是一黑一白,所以移动的过后,一定是一个骨牌的黑色位置变成了之前空的格子,且这个骨牌盖住的白色格子是固定的;于是移动骨牌,可以看作是将空的位置交换到相邻可交换的黑点上。

于是可以先建一个黑点的图,表示所有的移动操作,然后考虑那些特殊位置,不能漏出来等价于 s 到它没有路径;于是考虑这样建图,以初始空位为源点 s,建边流量为该骨牌的代价,对于特殊位置黑点,向汇点建容量为 +\infty 的边表示不能割掉;此时的最小割就是最小的代价断开 s 到那些特殊点路径的方案。

直接跑 dinic 即可,时间复杂度为 O(nm)

完整代码:

#include<bits/stdc++.h>
#define ls(k) k << 1
#define rs(k) k << 1 | 1
#define fi first
#define se second
#define popcnt(x) __builtin_popcount(x)
#define open(s1, s2) freopen(s1, "r", stdin), freopen(s2, "w", stdout);
using namespace std;
typedef __int128 __;
typedef long double lb;
typedef double db;
typedef unsigned long long ull;
typedef long long ll;
const int N = 1.1e6 + 10, M = 1e7 + 10;
const int INF = 1e9;
inline ll read(){
    ll x = 0, f = 1;
    char c = getchar();
    while(c < '0' || c > '9'){
        if(c == '-')
          f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9'){
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }
    return x * f;
}

inline void write(ll x){
    if(x < 0){
        putchar('-');
        x = -x;
    }
    if(x > 9)
      write(x / 10);
    putchar(x % 10 + '0');
}
namespace Dinic {
    struct edge {
        int v, c, lim, nxt;
    } e[M];
    int head[N], dep[N], now[N];
    int s, t, node, idx = 1;
    inline void add(int u, int v, int c) {
        e[++idx] = {v, c, c, head[u]};
        head[u] = idx;
    }
    inline void Add(int u, int v, int c) {
        // cerr << "edge: " << u << ' ' << v << ' ' << c << '\n';
        add(u, v, c);
        add(v, u, 0);
    }
    inline bool bfs() {
        memset(dep, 0, sizeof(int) * (node + 1));
        queue<int> q;
        q.push(s);
        dep[s] = 1;
        while(!q.empty()) {
            int u = q.front();
            q.pop();
            for(int i = head[u]; i; i = e[i].nxt) {
                int v = e[i].v, c = e[i].c;
                if(!dep[v] && c) {
                    dep[v] = dep[u] + 1;
                    q.push(v);
                    if(v == t)
                      return 1;
                }
            }
        }
        return 0;
    }
    inline ll dfs(int u, ll mf) {
        if(u == t)
          return mf;
        ll sum = 0;
        for(int i = now[u]; i; i = e[i].nxt) {
            now[u] = i;
            int v = e[i].v, c = e[i].c;
            if(dep[v] == dep[u] + 1 && c) {
                ll flow = dfs(v, min((ll)c, mf));
                e[i].c -= flow;
                e[i ^ 1].c += flow;
                sum += flow;
                mf -= flow;
                if(!mf)
                  break;
            }
        }
        if(!sum)
          dep[u] = 0;
        return sum;
    }
    inline ll dinic() {
        ll ans = 0;
        while(bfs()) {
            memcpy(now, head, sizeof(int) * (node + 1));
            ans += dfs(s, INF);
        }
        return ans;
    }
}
int n, m, k, s, t;
int w[N], h[N];
bool vis[N];
inline int id(int i, int j){
    return (i - 1) * m + j;
}
inline bool col(int i, int j){
    return (i + j - 1) & 1;
}
inline void add(int x, int y, int c){
    Dinic::Add(x, y, c);
    Dinic::Add(y, x, c);
}
int main(){
    n = read(), m = read(), k = read();
    Dinic::t = t = Dinic::node = n * m + 1;
    for(int i = 1; i <= (n * m - 1) >> 1; ++i)
      w[i] = read();
    while(k--){
        int x = read(), y = read();
        if(col(x, y))
          vis[id(x, y)] = 1;
    }
    for(int i = 1; i <= n; ++i){
        for(int j = 1; j <= m; ++j){
            h[id(i, j)] = read();
            if(h[id(i, j)] == h[id(i, j - 1)] && j > 1){
                if(col(i, j) && j > 2)
                  add(id(i, j), id(i, j - 2), w[h[id(i, j)]]);
                if(!col(i, j) && j + 1 <= m)
                  add(id(i, j - 1), id(i, j + 1), w[h[id(i, j)]]);
            }
            if(h[id(i, j)] == h[id(i - 1, j)] && i > 1){
                if(col(i, j) && i > 2)
                  add(id(i, j), id(i - 2, j), w[h[id(i, j)]]);
                if(!col(i, j) && i + 1 <= n)
                  add(id(i - 1, j), id(i + 1, j), w[h[id(i, j)]]);
            }
            if(!h[id(i, j)])
              s = id(i, j);
            if(col(i, j) && vis[id(i, j)])
              Dinic::Add(id(i, j), t, INF);
        }
    }
    if(vis[s]){
        puts("GG");
        return 0;
    }
    Dinic::s = s;
    write(Dinic::dinic());
    return 0;
}