BZOJ2756 [SCOI2012]奇怪的游戏 最大流

好久没有写博客了。不过这个博客也没有多少人看

最近在写网络流,为了加深理解,来写一两篇题解。

对整个棋盘进行黑白染色以后可以发现,一次操作就是让二分图的两个点的值分别 \(+1\)

这样,我们就可以对一个答案的合法性做出判断了。

对于每个白点,从 \(S\) 向它连 \(ans - a[i][j]\) 的边。黑点向 \(T\)\(ans - a[i][j]\) 的边。每个白点向黑点建 \(+\infty\) 的边。

如果满流就成立。

发现答案满足单调性,然后就开始非常开心地二分答案?

你会发现从 \(S\) 出来的容量和与流向 \(T\) 的容量和根本不一定等。

但是它们必须等。

于是我们设 \(cnt_1\) 为白点的数量,\(sum_1\) 为白点初始权值之和。\(cnt_0, sum_0\) 为黑点的。

那么必须有:
\[ ans \cdot cnt_0 - sum_0 = ans \cdot cnt_1 - sum_1 \]
可以化成
\[ (cnt_o - cnt_1) \cdot ans = sum_0 - sum_1 \]
如果 \(cnt_0 \neq cnt_1\) 可以直接把 \(ans\) 算出来验证一下就可以了。

如果 \(cnt_o = cnt_1\) 那么就是说 \(sum_o = sum_1\) 不满足就可以直接Skipped了。如果满足的话 \(ans\) 可以随便取值,那么就可以二分求出最小的了。

#include<bits/stdc++.h>

#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back

template<typename A, typename B> inline char SMAX(A &a, const B &b) {return a < b ? a = b , 1 : 0;}
template<typename A, typename B> inline char SMIN(A &a, const B &b) {return b < a ? a = b , 1 : 0;}

typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;

template<typename I>
inline void read(I &x) {
    int f = 0, c;
    while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
    x = c & 15;
    while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
    f ? x = -x : 0;
}

const int MAXN = 40 + 7;
const int N = MAXN * MAXN;
const int M = MAXN * MAXN * 3;
const int dx[] = {0, 1, 0, -1};
const int dy[] = {1, 0, -1, 0};
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int INF_int = 0x3f3f3f3f;

int n, m, S, T, nod, cnt0, cnt1, hd, tl, allsize;
ll sum0, sum1, sum;
int a[MAXN][MAXN], id[MAXN][MAXN], col[MAXN][MAXN];
int q[N], cur[N], dis[N];

struct Edge {int to, ne; ll f;} g[M << 1]; int head[N], tot = 1;
inline void addedge(int x, int y, ll z) {g[++tot].to = y; g[tot].f = z; g[tot].ne = head[x]; head[x] = tot;}
inline void adde(int x, int y, ll z) {addedge(x, y, z); addedge(y, x, 0);}

inline bool bfs() {
    memset(dis, 0x3f, allsize); memcpy(cur, head, allsize);
    q[hd = 0, tl = 1] = S; dis[S] = 0;
    while (hd < tl) {
        int x = q[++hd];
        for fec(i, x, y) if (g[i].f && dis[y] == INF_int) {
            dis[y] = dis[x] + 1; q[++tl] = y;
            // dbg("y = %d, T = %d\n", y, T);
            if (y == T) return 1;
        }
    }
    return 0;
}
inline ll dfs(int x, ll a) {
    // dbg("x = %d a = %lld\n", x, a);
    if (x == T || !a) return a;
    ll flow = 0, f;
    for (int &i = cur[x]; i; i = g[i].ne) {
        int y = g[i].to;
        if (dis[y] != dis[x] + 1) continue;
        if (!(f = dfs(y, std::min(a, g[i].f)))) continue;
        g[i].f -= f, g[i ^ 1].f += f;
        flow += f, a -= f; if (!a) break;
    }
    if (!flow) dis[x] = INF_int;
    return flow;
}
inline ll dinic() {
    ll ans = 0;
    while (bfs()) ans += dfs(S, INF);
    // dbg("ans = %lld\n", ans);
    return ans;
}

inline bool build(ll mid) {
    memset(head, 0, sizeof(head));
    tot = 1; sum = 0;
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j) {
            if (mid < a[i][j]) return 0;
            if (col[i][j]) {
                adde(S, id[i][j], mid - a[i][j]);
                sum += mid - a[i][j];
                for (int k = 0; k < 4; ++k) {
                    int px = i + dx[k], py = j + dy[k];
                    if (px < 1 || px > n || py < 1 || py > m) continue;
                    adde(id[i][j], id[px][py], INF);
                }
            } else adde(id[i][j], T, mid - a[i][j]);
        }
    return 1;
}

inline bool check(ll mid) {
    if (!build(mid)) return 0;
    return dinic() == sum;
}

inline void work() {
    if (cnt0 == cnt1) {
        if (sum0 != sum1) {
            puts("-1");
            return;
        } else {
            ll l = 0, r = INF;
            while (l < r) {
                ll mid = (l + r) >> 1;
                if (check(mid)) r = mid;
                else l = mid + 1;
            }
            // dbg("l = %d\n", l);
            // for (ll i = l - 10000; i <= l + 10000; ++i)
                // dbg("When i = %lld, chk = %d\n", i, (int)check(i));
            if (l < INF) printf("%lld\n", l * cnt0 - sum0);
            else puts("-1");
        }
    } else {
        ll dsum = sum0 - sum1;
        int dcnt = cnt0 - cnt1;
        if (((dsum < 0 && dcnt < 0) || (dsum > 0 && dcnt > 0)) && dsum % dcnt == 0) {
            ll ans = dsum / dcnt;
            if (check(ans)) printf("%lld\n", ans * cnt0 - sum0);
            else puts("-1");
        }
    }
}

inline void cls() {
    sum0 = sum1 = 0;
    cnt0 = cnt1 = 0;
}

inline void init() {
    cls();
    read(n), read(m);
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            read(a[i][j]);
            col[i][j] = (i + j) & 1;
            if (col[i][j]) ++cnt1, sum1 += a[i][j];
            else ++cnt0, sum0 += a[i][j];
            if (j > 1) id[i][j] = id[i][j - 1] + 1;
            else id[i][j] = id[i - 1][m] + 1;
        }
    }
    S = id[n][m] + 1, T = nod = id[n][m] + 2;
    allsize = (nod + 1) * sizeof(int);
}

int main() {
    #ifdef hzhkk
    freopen("hkk.in", "r", stdin);
    #endif
    int T; read(T);
    while (T--) {
        init();
        work();
    }
    fclose(stdin);
}

转载于:https://www.cnblogs.com/hankeke/p/10699010.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值