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();}
posted @ 2021-05-29 15:26  mizu164  阅读(161)  评论(0编辑  收藏  举报