Loading

CF1416F Showing Off

万物皆可匈牙利!

首先这道题有几个好想的性质,对于一个位置 \((i,j)\),这个位置连的另一个能到达的位置这个位置同样能够到达。所以,如果四周存在 \(S_{x,y}<S_{i,j}\),那么我们可以直接将 \((i,j)\) 连边到 \((x,y)\)\(A_{i,j}\leftarrow S_{i,j}-S_{x,y}\)。我们将这样的位置 \((i,j)\) 设为白点,否则为黑点。还有一种情况就是在同一个环内的位置,而我们可以注意到这样的环一定是偶环,所以我们只考虑二元环是一定不劣的。

对于 \(S_{i,j}\) 相同的位置,组二元环等价于一个匹配问题。显然所有黑点都需要有一个二元环包括。我们对矩形黑白染色,那么这张图是一张二分图。它相当于是这张二分图有黑点和白点,我们需要所有的黑点都在一个匹配内。虽然这个是一个经典的上下界问题,但是匈牙利是更优美的算法(一定不是我没有想到上下界网络流),所以我准备用匈牙利做这道题。本以为我能很快切掉,没想到噩梦才刚刚开始。

首先我想到了个很假的做法,就是不论是边顺序还是搜索顺序,都是黑在前白在后,然后跑二分图最大匹配。但是这显然有问题,因为我们并不要求最大匹配!我们考虑左部点右部点同时跑最大匹配,只要找到一个黑点,我们就进入搜索,首先前半部分可以跟最大匹配一样。而如果我们找不到最大匹配,如果我们存在与自己相邻的而匹配点为白点的,也可以更换匹配点并且判为增广成功,这样就可以找到可行的匹配。

交上去发现匈牙利被卡了,但都是些雕虫小技,直接随一个加入顺序就可以让匈牙利飞快通过~

代码:

#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 pii pair <int, int>
#define eb emplace_back
#define ls p << 1
#define rs ls | 1
#define inf 1000000000
#define id(x, y) (x - 1) * m + (y)
using namespace std;
constexpr int N = 2e5 + 5;
typedef unsigned long long ull;
typedef long long ll;
inline ll rd () {
  ll 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;
}
int n, m;
vector <int> a[N], b[N], e[N];
vector <char> c[N];
int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0};
char ch[4] = {'R', 'D', 'L', 'U'};
bool no[N];
bool vis[N];
int match[N], stk[N], top;
bool dfs (int u) {
  for (auto v : e[u]) {
    if (vis[v]) continue;
    vis[v] = 1; stk[++ top] = v;
    if (! match[v] || dfs (match[v])) {
      match[v] = u, match[u] = v;
      return 1;
    }
    if (! no[match[v]]) {
      match[match[v]] = 0;
      match[v] = u, match[u] = v;
      return 1;
    }
  }
  return 0;
}
int p[N], sum;
bool cmp (int x, int y) {
  return no[x] > no[y];
}
mt19937 rnd (time (0));
void solve () {
  n = rd (), m = rd ();
  rep (i, 1, n * m) match[i] = 0, e[i].clear (), no[i] = 0;
  rep (i, 1, n) {
    a[i].resize (m + 2);
    b[i].resize (m + 2);
    c[i].resize (m + 2);
    rep (j, 1, m) b[i][j] = rd ();
  }
  int cnt = 0;
  bool fl = 0;
  rep (i, 1, n) {
    rep (j, 1, m) {
      int mn = 2e9;
      bool ok = 0;
      rep (k, 0, 3) {
        int x = dx[k] + i, y = dy[k] + j;
        if (x < 1 || y < 1 || x > n || y > m) continue;
        mn = min (mn, b[x][y]);
        if (b[x][y] < b[i][j]) {
          a[i][j] = b[i][j] - b[x][y];
          c[i][j] = ch[k]; ok = 1;
          break;
        }
      }
      if (ok) continue;
      if (mn > b[i][j]) fl = 1;
      no[id (i, j)] = 1; ++ cnt;
    }
  }
  if (fl) return void (puts ("NO"));
  rep (i, 1, n) {
    rep (j, 1, m) {
      if (j < m) {
        if (b[i][j] == b[i][j + 1]) {
          e[id (i, j)].eb (id (i, j + 1));
          e[id (i, j + 1)].eb (id (i, j));
        }
      }
      if (i < n) {
        if (b[i][j] == b[i + 1][j]) {
          e[id (i, j)].eb (id (i + 1, j));
          e[id (i + 1, j)].eb (id (i, j));
        }
      }
    }
  }
  rep (i, 1, n * m) p[i] = i;
  shuffle (p + 1, p + n * m + 1, rnd);
  rep (i, 1, n * m) {
    if (! no[p[i]] || match[p[i]]) continue;
    if (! dfs (p[i])) break;
    while (top) vis[stk[top --]] = 0;
  }
  bool flag = 1;
  rep (i, 1, n) {
    rep (j, 1, m) {
      if (! match[id (i, j)] && no[id (i, j)]) {
        flag = 0; break;
      }
      if (match[id (i, j)] == id (i, j + 1)) { 
        a[i][j] = 1, a[i][j + 1] = b[i][j] - 1;
        c[i][j] = 'R', c[i][j + 1] = 'L';
      } 
      if (match[id (i, j)] == id (i + 1, j)) {
        a[i][j] = 1, a[i + 1][j] = b[i][j] - 1;
        c[i][j] = 'D', c[i + 1][j] = 'U';
      }
    }
  }
  if (! flag) return void (puts ("NO"));
  puts ("YES");
  rep (i, 1, n) {
    rep (j, 1, m) {
      printf ("%d ", a[i][j]);
    } puts ("");
  }
  rep (i, 1, n) {
    rep (j, 1, m) {
      putchar (c[i][j]), putchar (' ');
    } puts ("");
  }
}
int main () {
  // freopen ("1.in", "r", stdin);
  // freopen ("1.out", "w", stdout);
  for (int T (rd ()); T; -- T) solve ();
  cerr << 1.0 * clock () / CLOCKS_PER_SEC << endl;
}
posted @ 2024-11-14 15:24  lalaouye  阅读(3)  评论(0编辑  收藏  举报