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;
}