[博弈论][二分图匹配][网络流] LOJ #536. 「LibreOJ Round #6」花札

12 篇文章 0 订阅
12 篇文章 0 订阅

Solution S o l u t i o n

这是一个Undirected Vertex Geography
可以 O(n3) O ( n 3 ) 对每个点得到答案。
UVG游戏中 (G,v) ( G , v ) 即图 G G 中先手在 v 必胜的充要条件是 v v 在所有最大匹配中。
证明:

  • (充分性:)假设 v 在包含它的最大匹配 M M 中:
    • 如果他在必败态,则会走这样的路径 {e1,f1,e2,f2,...,ek,fk} ,满足 ve1,eiM,fiM,i=1,2...,k v ∈ e 1 , e i ∈ M , f i ∉ M , i = 1 , 2... , k 。并且 fk=(x,y) f k = ( x , y ) ,其中 y y 不被 M 包含。设 A={e1,e2...,ek},B={f1,f2...,fk} A = { e 1 , e 2 . . . , e k } , B = { f 1 , f 2 . . . , f k } 一个直观的结论就是边集 M=(MA)B M ′ = ( M ∖ A ) ∪ B 是一个不包含 v v 的匹配,且边数与 M 相同,即 M M ′ 不包含 v v ,与假设矛盾。
  • (必要性:)假设存在最大匹配 M 不包含 v v
    • 如果他在必胜态,第一步走边 (v,w) ,则 w w 一定在最大匹配 M 中。则等价于后手在最大匹配 M M 中必败,则会走这样的路径 {e1=(v,w),f1,e2,f2,...,ek,fk,ek+1=(x,y)} ,满足 y y 不被 M 包含。事实上我们得到了一个新的最大匹配 M={e1,e2,...,ek+1} M ′ = { e 1 , e 2 , . . . , e k + 1 } ,与假设矛盾。

      考虑优化判断一个点是否一定在最大匹配中的方法。
      注意到一个点 i i 一定在最大匹配中等价于所有最大流中 S 到该点 i i 的边有流量。
      这个命题等价于:在某个最大流中 S i i 的边有流量,且残量网络上不存在 S i i <script type="math/tex" id="MathJax-Element-9398">i</script> 的路径。

      直接在残量网络里搜索就好啦。。。

      #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;
      }
      
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值