Loading

utpc2024_e Edge Coloring Problem

这道题适合在没事情做的时候用一上午,一下午或者是一晚上去学,非常带派。

边染色问题的另一个理解方式是匹配覆盖,或者说将图拆分成若干组匹配。而最小化颜色数量自然也相当于最小化匹配组数。

考虑通过下界猜测颜色数,首先一个下界是 \(\max \deg\),即 \(n-3\)。但是你发现 \(n=5\) 的时候它答案就是 \(3\)。我们考虑匹配,有 \(\frac{n(n-3)}{2}\) 条边,我们一次匹配最多只能吃掉 \(\lfloor\frac n 2\rfloor\) 条边,于是你发现 \(n\) 为奇数时的下界是 \(n-2\),接下来我们都能把它们构造出来。

此时你要解决两个问题,分别对应了 \(n\) 为奇数和 \(n\) 为偶数的情况。我们在思考 \(n\) 为奇数时可以发现,从感性上将它的限制是相对比较松的,我们可以直接加一个点,然后直接规约到 \(n\) 为偶数的情况,这种规约思想是非常常用的。具体怎么加呢?你找 \(n-2\) 个点跟新点连,剩下两个点需要满足它们之间没有边,然后我们把这条边加上,这样我们只需要解决 \(n\) 为偶数的情况了。

我们很难直接从整张图入手,我们考察这张图的补图。补图是由非常规则的若干简单换组成,对应到原图的意义是不同环之间的点一定有边,环上不相邻两点有边。这样看起来好做很多,但还是无法下手。

我们希望将图刻画成更简单我们更能接受的样子。我们考虑如果这张图全是偶环,那么我们可以黑白染色变成一张二分图。不妨设 \(M=\frac n 2\),此时原图中每一部都是一张完全图,而中间的边则是度数为 \(M-2\) 的正则二分图!

对于正则二分图我们可以轻松求出它的 \(M-2\) 组匹配,对于完全图我们也可以利用数论同余构造的技巧轻松构造合法的边染色方案。当 \(M\) 为奇数时,其边染色需要 \(M\) 种颜色,而 \(M\) 为偶数时,只需要 \(M-1\) 种颜色。

这里我们也可以去规约,而我们需要注意规约的方向,这里如果我们将奇数规约到偶数是做不了的,我们将偶数规约至奇数。规约方式是,我们不难证明在 \(M\) 为奇数时,每个点连的边都至少存在一种颜色没有用到,且所有点这样的颜色互不相同。所以我们直接在最后加入这么一个点,然后之间的颜色就用 \(M\) 为奇数时每个点缺少的颜色即可完成规约。

对于 \(M\) 为奇数的情况,构造时不妨还是将视角放在匹配上。对于第 \(i\) 组匹配,我们将 \(x+y\equiv 2i\pmod M\)\(x,y\) 连边匹配。

然而,对于 \(M\) 为偶数的情况,我们直接合并就是 \(M-2+M-1=n-3\) 组匹配,而如果 \(M\) 为奇数我们直接合并只能做到 \(n-2\)。不过由于我们发现完全图第 \(i\) 组匹配 \(i\) 一定是孤立点,我们可以在合并左右两面匹配时同时吃掉二分图中的一组匹配。即我们随便找一组匹配,对于匹配边 \((L_i,R_i)\),我们将左部第 \(L_i\) 组匹配右部第 \(R_i\) 组匹配和 \((L_i,R_i)\) 合并至同一组匹配加入答案。

不过很遗憾这题可以存在奇环,不过我们刚刚的刻画是很优美的,我们看看能不能利用刚刚的做法通过一些修改完成一般情况的问题。

首先由于 \(n\) 为偶数,那么奇环数量一定是偶数个。我们依旧尽可能黑白染色,然后此时我们会有 \(2k\) 个无法染色的对,我们设其为 \((x_1,y_1),\cdots,(x_{2k},y_{2k})\)。我们将其中 \([1,k]\) 的放入左部,\([k+1,2k]\) 放入右部。注意放入前对于它们所在的奇环也要随之调整。

此时左右部点数相同,在原图中它们是完全图少了若干不交的边,而之间是正则二分图加入了若干边,我们令这些边为 \((x_1,x_{k+1}),(y_1,y_{k+1}),\cdots,(x_k,x_{2k}),(y_k,y_{2k})\),于是我们不妨先将这些边删除,将左右部中少的边加入。由于我们在构造完全图匹配的时候,我们其实是有很高的自由度的。我们可以通过改变编号控制将新加入的边放入同一匹配,这样我们将两边的这一匹配合并时,就可以手动将这些点的匹配边删掉,然后将 \((x_1,x_{k+1})\) 这些边加入匹配,就完成了构造。

不过对于 \(M\) 为奇数的情况需要注意它们这些特殊的边在的匹配中,左右部的孤立点需要有边,不然我们没办法把它们放在同一匹配中。

理论上复杂度可以做到 \(\mathcal{O}(n^3)\),不过我懒得写那个二分图 \(\mathcal{O}(n^3)\) 边染色的算法了,直接匈牙利 \(\mathcal{O}(n^{3.5})\) 完全能过,就没有匈牙利过不了的东西!

#include <bits/stdc++.h>
#define rep(i, l, r) for (int i (l); i <= (r); ++ i)
#define rrp(i, l, r) for (int i (r); i >= (l); -- i)
#define eb emplace_back
using namespace std;
#define pii pair <int, int>
#define inf 1000000000000000
#define ls (p << 1)
#define rs (ls | 1)
#define fi first
#define se second
#define re register int
constexpr int N = 300 + 5, M = 1e5 + 5, P = 998244353, B = 1000;
typedef long long ll;
typedef unsigned long long ull;
inline int rd () {
  int x = 0, f = 1;
  char ch = getchar ();
  while (! isdigit (ch)) {
    if (ch == '-') f = -1;
    ch = getchar ();
  }
  while (isdigit (ch)) {
    x = (x << 1) + (x << 3) + (ch ^ 48);
    ch = getchar ();
  }
  return x * f;
}
inline int qpow (int x, int y, int p = P) {
  int ret (1);
  for (; y; y >>= 1, x = x * x % p) if (y & 1) ret = ret * x % p;
  return ret;
}
int a[N][N], n, m;
int c[N][N];
char s[N];
vector <int> vec;
bool vis[N];
void Dfs (int u) {
  if (vis[u]) return ;
  vis[u] = 1;
  vec.eb (u);
  rep (i, 1, n) if (! a[u][i]) Dfs (i);
}
vector <int> L, R;
int ma[N];
vector <vector <pii> > ret;
int match[N];
int col[N];
int p[N], q[N];
bool cut[N][N];
bool C[N][N];
int ans[N][N], fx, fy;
bool dfs (int u) {
  rep (i, 1, n) {
    if (! a[u][i] || vis[i] || col[u] == col[i] || cut[u][i] || C[u][i]) continue;
    vis[i] = 1;
    if (! match[i] || dfs (match[i])) return match[i] = u, match[u] = i, 1;
  } return 0;
}
void remark (int x, int y) {
  int tot (0);
  p[x] = 0;
  for (auto u : L) {
    if (u < ma[u]) {
      ++ tot;
      p[u] = (m - tot) % m;
      p[ma[u]] = tot % m;
    }
  }
  for (auto u : L) {
    if (u != x && ! ma[u]) ++ tot, p[u] = tot % m;
  }
  tot = 0;
  q[y] = 0;
  for (auto u : R) {
    if (u < ma[u]) {
      ++ tot;
      q[u] = (m - tot) % m;
      q[ma[u]] = tot % m;
    }
  }
  for (auto u : R) {
    if (u != y && ! ma[u]) ++ tot, q[u] = tot % m;
  }
}
int32_t main () {
  // freopen ("1.in", "r", stdin);
  // freopen ("1.out", "w", stdout);
  n = rd ();
  bool fl (0);
  rep (i, 1, n) {
    scanf ("%s", s + 1);
    rep (j, 1, n) a[i][j] = s[j] & 1;
  }
  fl = n & 1;
  if (fl) {
    ++ n;
    bool flag (0);
    rep (i, 1, n - 1) rep (j, i + 1, n - 1) {
      if (flag) break;
      if (! a[i][j]) {
        a[i][j] = a[j][i] = 1;
        fx = i, fy = j;
        rep (k, 1, n - 1) if (k != i && k != j) a[k][n] = a[n][k] = 1;
        flag = 1; break;
      }
    }
  }
  rep (i, 1, n) {
    int cnt (0);
    rep (j, 1, n) cnt += a[i][j];
    if (cnt != n - 3) while (1);
  }
  rep (i, 1, n) {
    if (! vis[i]) {
      vec.clear ();
      Dfs (i);
      if (vec.size () & 1) {
        if (L.size () > R.size ()) swap (L, R);
        rep (i, 0, vec.size () - 3) if (i & 1) L.eb (vec[i]); else R.eb (vec[i]);
        int x (vec.back ()); vec.pop_back ();
        int y (vec.back ());
        L.eb (x), L.eb (y);
        ma[x] = y, ma[y] = x;
      } else rep (i, 0, vec.size () - 1) if (i & 1) R.eb (vec[i]); else L.eb (vec[i]);
    }
  }
  assert (L.size () == R.size ());
  m = n >> 1;
  for (auto u : L) col[u] = 0;
  for (auto u : R) col[u] = 1;
  sort (L.begin (), L.end (), [&] (int x, int y) { return ma[x] > ma[y]; });
  sort (R.begin (), R.end (), [&] (int x, int y) { return ma[x] > ma[y]; });
  int tl (0), tr (0);
  if (~ m & 1) {
    -- m; tl = L.back (), tr = R.back ();
    L.pop_back (), R.pop_back (); 
  }
  int x = L[0], y = R[0];
  for (auto u : L) for (auto v : R) if (! ma[u] && ! ma[v] && a[u][v]) x = u, y = v;
  remark (x, y);
  memset (ans, -1, sizeof ans);
  if (tl) {
    rep (i, 0, m - 1) {
      if (! ma[L[i]]) break;
      assert (a[L[i]][R[i]]);
      C[L[i]][R[i]] = C[R[i]][L[i]] = 1;
    }
    rep (i, 0, m - 1) {
      vector <pii> vec;
      for (auto u : L) for (auto v : L) if (u < v && (p[u] + p[v]) % m == i * 2 % m) vec.eb (pii (u, v));
      for (auto u : R) for (auto v : R) if (u < v && (q[u] + q[v]) % m == i * 2 % m) vec.eb (pii (u, v));
      for (auto u : L) if (p[u] == i) vec.eb (pii (tl, u));
      for (auto u : R) if (q[u] == i) vec.eb (pii (tr, u));
      ret.eb (vec);
    }
    rep (i, 1, m - 1) {
      vector <pii> vec;
      memset (match, 0, sizeof match);
      rep (j, 1, n) {
        if (col[j]) continue;
        memset (vis, 0, sizeof vis);
        assert (dfs (j));
      }
      rep (j, 1, n) if (j < match[j]) vec.eb (pii (j, match[j])), cut[j][match[j]] = cut[match[j]][j] = 1;
      ret.eb (vec);
    }
    assert (ret.size () == n - 3);
    rep (i, 0, n - 4) {
      for (auto p : ret[i]) {
        ans[p.first][p.second] = ans[p.second][p.first] = i + 1;
      }
    }
    rep (i, 1, n) rep (j, 1, n) if (! a[i][j]) ans[i][j] = -1; else if (C[i][j]) ans[i][j] = 1;
  } else {
    int id (0);
    rep (i, 0, m - 1) {
      if (! ma[L[i]]) break;
      assert (a[L[i]][R[i]]);
      C[L[i]][R[i]] = C[R[i]][L[i]] = 1;
    }
    rep (i, 1, m - 2) {
      memset (match, 0, sizeof match);
      for (auto u : L) memset (vis, 0, sizeof vis), assert (dfs (u));
      vector <pii> vec;
      bool flag (0);
      for (auto u : L) if (u == x && match[u] == y) flag = 1;
      if (flag) {
        for (auto U : L) {
          vector <pii> vec; 
          for (auto u : L) for (auto v : L) if (u < v && (p[u] + p[v]) % m == p[U] * 2 % m) vec.eb (pii (u, v));
          for (auto u : R) for (auto v : R) if (u < v && (q[u] + q[v]) % m == q[match[U]] * 2 % m) vec.eb (pii (u, v));
          vec.eb (pii (U, match[U]));
          cut[U][match[U]] = cut[match[U]][U] = 1;
          ret.eb (vec);
          if (U == x) id = ret.size ();
        }
      } else {
        for (auto u : L) {
          vec.eb (pii (u, match[u]));
          cut[u][match[u]] = cut[match[u]][u] = 1;
        }
        ret.eb (vec);
      }
    }
    rep (i, 0, n - 4) {
      for (auto p : ret[i]) {
        ans[p.first][p.second] = ans[p.second][p.first] = i + 1;
      }
    }
    rep (i, 1, n) rep (j, 1, n) if (! a[i][j]) ans[i][j] = -1; else if (C[i][j]) ans[i][j] = id;
  }
  if (fl) -- n, ans[fx][fy] = ans[fy][fx] = -1;
  cout << n - 3 + (n & 1) << endl;
  rep (i, 1, n) {
    rep (j, 1, n) printf ("%d ", ans[i][j]); puts ("");
  } 
}
posted @ 2025-12-27 09:42  lalaouye  阅读(4)  评论(0)    收藏  举报