CF1284G Seollal【拟阵交】
给定 \(n\times m\) 的网格图挖掉一些点,构造黑白染色时除 \((1,1)\) 之外的叶子都与 \((1,1)\) 不同色的生成树。
\(100\) 组 \(n,m\le 10\) 或 \(1\) 组 \(n,m\le 20\)。保证有解。
这是一份拟阵交板子。
构造两个拟阵 \(M_1\):无环,\(M_2\):除 \((1,1)\) 之外的黑色点度数 \(\le 2\)。
求个拟阵交,如果有除 \((1,1)\) 外的黑色点度数 \(<2\) 则无解。否则可以随便加一些边变成一棵生成树。
然后对着算法流程搞就完事了。
#include<bits/stdc++.h>
#define PB emplace_back
#define MP make_pair
#define fi first
#define se second
using namespace std;
typedef pair<int, int> pii;
const int N = 803, d[4][2] = {{0, 1}, {0, -1}, {-1, 0}, {1, 0}};
int T, n1, n2, n, m, deg[N], q[N], pre[N], fr, re;
char g[21][21], ans[41][41];
int id(int x, int y){return x * n2 + y;}
namespace DSU {
int fa[N];
void init(int n){
for(int i = 0;i < n;++ i) fa[i] = i;
}
int getf(int x){
return x == fa[x] ? x : fa[x] = getf(fa[x]);
}
bool comb(int u, int v){
u = getf(u); v = getf(v);
if(u != v){
fa[u] = v;
return true;
}
return false;
}
}
pii E[N];
bool vis[N], in1[N], in2[N], dis[N];
void add_edge(int x, int y){
for(int i = 0;i < 4;++ i){
int nx = x + d[i][0], ny = y + d[i][1];
if(nx >= 0 && nx < n1 && ny >= 0 && ny < n2 && g[nx][ny] == 'O')
E[m++] = MP(id(x, y), id(nx, ny));
}
}
bool doit(){
memset(dis, 0, sizeof dis);
memset(pre, -1, sizeof pre);
memset(deg, 0, sizeof deg);
DSU::init(n);
for(int i = 0;i < m;++ i) if(vis[i]){
DSU::comb(E[i].fi, E[i].se);
++ deg[E[i].fi];
}
fr = re = 0;
for(int i = 0;i < m;++ i){
in2[i] = !vis[i] && deg[E[i].fi] < 2;
if(in1[i] = !vis[i] && DSU::getf(E[i].fi) != DSU::getf(E[i].se)){
if(in2[i]) return vis[i] = true;
dis[i] = true; q[re++] = i;
}
}
while(fr < re){
int u = q[fr++];
if(in2[u]){
while(~u){
vis[u] ^= 1;
u = pre[u];
}
return true;
}
if(vis[u]){
DSU::init(n);
for(int i = 0;i < m;++ i)
if(vis[i] && i != u)
DSU::comb(E[i].fi, E[i].se);
for(int v = 0;v < m;++ v)
if(!vis[v] && !dis[v] && DSU::getf(E[v].fi) != DSU::getf(E[v].se)){
q[re++] = v; dis[v] = true; pre[v] = u;
}
} else
for(int v = 0;v < m;++ v)
if(vis[v] && !dis[v] && deg[E[u].fi] - (E[u].fi == E[v].fi) < 2){
q[re++] = v; dis[v] = true; pre[v] = u;
}
}
return false;
}
void solve(){
memset(vis, 0, sizeof vis);
memset(ans, 0, sizeof ans);
scanf("%d%d", &n1, &n2); n = n1 * n2;
for(int i = 0;i < n1;++ i)
scanf("%s", g[i]);
int _ = m = 0;
for(int i = 0;i < n1;++ i)
for(int j = !i;j < n2;++ j)
if(g[i][j] == 'O' && !(i + j & 1))
add_edge(i, j), _ += 2;
while(doit()) -- _;
if(_){puts("NO"); return;}
puts("YES");
add_edge(0, 0);
DSU::init(n);
for(int i = 0;i < m;++ i)
if(vis[i]) DSU::comb(E[i].fi, E[i].se);
for(int i = 0;i < m;++ i)
if(!vis[i] && DSU::comb(E[i].fi, E[i].se))
vis[i] = true;
for(int i = 0;i <= (n1-1<<1);++ i)
for(int j = 0;j <= (n2-1<<1);++ j)
ans[i][j] = ' ';
for(int i = 0;i < n1;++ i)
for(int j = 0;j < n2;++ j)
ans[i<<1][j<<1] = g[i][j];
for(int i = 0;i < m;++ i) if(vis[i]){
int mn = min(E[i].fi, E[i].se), mx = max(E[i].fi, E[i].se);
if(mn+1 == mx) ans[mn/n2<<1][mn%n2<<1|1] = 'O';
else ans[mn/n2<<1|1][mn%n2<<1] = 'O';
}
for(int i = 0;i <= (n1-1<<1);++ i)
printf("%s\n", ans[i]);
}
int main(){scanf("%d", &T); while(T --) solve();}