[博弈論][二分圖匹配][網絡流] LOJ #536. 「LibreOJ Round #6」花札

Solution

這是一個Undirected Vertex Geography
可以 O(n3) 對每個點得到答案。
UVG遊戲中 (G,v) 即圖 G 中先手在 v 必勝的充要條件是 v 在所有最大匹配中。
證明:

  • (充分性:)假設 v 在包含它的最大匹配 M 中:
    • 如果他在必敗態,則會走這樣的路徑 {e1,f1,e2,f2,...,ek,fk} ,滿足 ve1,eiM,fiM,i=1,2...,k 。並且 fk=(x,y) ,其中 y 不被 M 包含。設 A={e1,e2...,ek},B={f1,f2...,fk} 一個直觀的結論就是邊集 M=(MA)B 是一個不包含 v 的匹配,且邊數與 M 相同,即 M 不包含 v ,與假設矛盾。
  • (必要性:)假設存在最大匹配 M 不包含 v
    • 如果他在必勝態,第一步走邊 (v,w) ,則 w 一定在最大匹配 M 中。則等價於後手在最大匹配 M 中必敗,則會走這樣的路徑 {e1=(v,w),f1,e2,f2,...,ek,fk,ek+1=(x,y)} ,滿足 y 不被 M 包含。事實上我們得到了一個新的最大匹配 M={e1,e2,...,ek+1} ,與假設矛盾。

考慮優化判斷一個點是否一定在最大匹配中的方法。
注意到一個點 i 一定在最大匹配中等價於所有最大流中 S 到該點 i 的邊有流量。
這個命題等價於:在某個最大流中 Si 的邊有流量,且殘量網絡上不存在 Si 的路徑。

直接在殘量網絡裏搜索就好啦。。。

#include <bits/stdc++.h>
#define show(x) cerr << #x << " = " << x << endl
using namespace std;
typedef long long ll;
typedef pair<int, int> Pairs;

const int N = 1010101;
const int INF = 1 << 30;

inline char get(void) {
    static char buf[100000], *S = buf, *T = buf;
    if (S == T) {
        T = (S = buf) + fread(buf, 1, 100000, stdin);
        if (S == T) return EOF;
    }
    return *S++;
}
template<typename T>
inline void read(T &x) {
    static char c; x = 0; int sgn = 0;
    for (c = get(); c < '0' || c > '9'; c = get()) if (c == '-') sgn = 1;
    for (; c >= '0' && c <= '9'; c = get()) x = x * 10 + c - '0';
    if (sgn) x = -x;
}

struct edge {
    int to, cap, next;
    edge(int t = 0, int c = 0, int n = 0): to(t), cap(c), next(n) {}
};
edge G[N << 1];
int head[N], cur[N], dis[N], vis[N];
int id[N], ans[N];
int m, c, n1, n2, gcnt, s, t, clc, ncnt, x, y;
queue<int> Q;

inline void addEdge(int from, int to, int cap) {
    G[++gcnt] = edge(to, cap, head[from]); head[from] = gcnt;
    G[++gcnt] = edge(from, 0, head[to]); head[to] = gcnt;
}
inline int bfs(int s, int t) {
    vis[s] = ++clc;
    Q.push(s); dis[s] = 0;
    while (!Q.empty()) {
        int x = Q.front(); Q.pop();
        for (int i = head[x]; i; i = G[i].next) {
            edge &e = G[i];
            if (e.cap && vis[e.to] != clc) {
                dis[e.to] = dis[x] + 1;
                Q.push(e.to); vis[e.to] = clc;
            }
        }
    }
    return vis[t] == clc;
}
inline int dfs(int u, int a) {
    if (u == t || a == 0) return a;
    int flow = 0, f;
    for (int &i = cur[u]; i; i = G[i].next) {
        edge &e = G[i];
        if (e.cap && dis[e.to] == dis[u] + 1
                && (f = dfs(e.to, min(a, e.cap))) > 0) {
            e.cap -= f; G[i ^ 1].cap += f;
            flow += f; a -= f;
            if (a == 0) break;
        }
    }
    return flow;
}
inline int maxFlow(int s, int t) {
    int flow = 0;
    while (bfs(s, t)) {
        for (int i = 0; i <= ncnt; i++) cur[i] = head[i];
        flow += dfs(s, INF);
    }
    return flow;
}
inline void dfs(int u) {
    vis[u] = clc; ans[id[u]] = 0;
    for (int i = head[u]; i; i = G[i].next) {
        edge &e = G[i];
        if (e.cap < 0 && !vis[e.to])
            dfs(e.to);
    }
}

int main(void) {
    gcnt = 1;
    freopen("1.in", "r", stdin);
    freopen("1.out", "w", stdout);
    read(m); read(c); ncnt = m + c;
    s = ++ncnt; t = ++ncnt;
    read(n1);
    for (int i = 1; i <= n1; i++) {
        ans[i] = 1;
        read(x); read(y);
        addEdge(s, ++ncnt, 1);
        addEdge(ncnt, x, 1);
        addEdge(ncnt, m + y, 1);
        id[ncnt] = i;
    }
    read(n2);
    for (int i = 1; i <= n2; i++) {
        read(x); read(y);
        addEdge(x, ++ncnt, 1);
        addEdge(m + y, ncnt, 1);
        addEdge(ncnt, t, 1);
    }
    maxFlow(s, t);
    for (int i = head[s]; i; i = G[i].next)
        if (G[i].cap == 1)
            ans[id[G[i].to]] = 0;
    for (int i = 1; i <= ncnt; i++)
        if (vis[i] == clc)
            ans[id[i]] = 0;
    for (int i = 1; i <= n1; i++)
        printf("%d\n", ans[i]);
    return 0;
}
發佈了231 篇原創文章 · 獲贊 375 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章