题解 启程的日子

传送门

一个我以为的转化是将同色连通块缩点
那么就是求最少用多少个白点可以将所有黑点连通
然而一个格子可以被加两次再减一次,所以就假了

那么观察(?)发现操作次数 \(\leqslant 3\),证明见 3 时的构造方法
为 0 和 1 直接算黑连通块个数即可
为 2 检查是否存在一个白连通块连通了所有黑点
为 3 需要一些构造:
image

前两次操作以这样一个划分为基础
image

第一次操作将黄,绿 +1
第二次将红,蓝 +1
第三次将除棕色部分外全部 -1
正确性显然,鬼知道怎么想到的

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 510
#define fir first
#define sec second
#define ll long long
//#define int long long

int n, m;
char s[N][N];
bool vis[N*N], rev;
map<int, bool> to[N*N];
set<pair<int, int>> pts[N*N];
int id[N][N], dsu[N*N], tot, cnt;
const int dlt[][2]={{-1,0},{1,0},{0,1},{0,-1}};
inline int find(int p) {return dsu[p]==p?p:dsu[p]=find(dsu[p]);}
inline void uni(int s, int t) {
	s=find(s), t=find(t);
	if (s==t) return ;
	if (pts[s].size()>pts[t].size()) swap(pts[s], pts[t]);
	for (auto it:pts[s]) pts[t].insert(it);
	dsu[s]=t;
}

namespace force{
	int sta[N], top, ans=INF, mini;
	int tem[N], dsu2[N], usiz;
	inline int find2(int p) {return dsu2[p]==p?p:dsu2[p]=find2(dsu2[p]);}
	void solve() {
		memset(vis, 0, sizeof(vis));
		for (int i=1; i<=n; ++i)
			for (int j=1; j<=m; ++j)
				if (!vis[find(id[i][j])]) {
					if (!s[i][j]) sta[top++]=find(id[i][j]);
					else tem[++usiz]=find(id[i][j]);
					vis[find(id[i][j])]=1;
				}
		// cout<<"---sta---"<<endl;
		// for (int i=0; i<top; ++i) {for (auto it:pts[sta[i]]) cout<<"("<<it.fir<<','<<it.sec<<") "; cout<<endl;}
		// cout<<"---tem---"<<endl;
		// for (int i=1; i<=usiz; ++i) {for (auto it:pts[tem[i]]) cout<<"("<<it.fir<<','<<it.sec<<") "; cout<<endl;}
		int lim=1<<top;
		for (int s=0; s<lim; ++s) {
			for (int i=1; i<=tot; ++i) dsu2[i]=i;
			for (int i=0; i<top; ++i) if (s&(1<<i)) {
				for (auto it:to[sta[i]]) {
					int v=it.fir;
					dsu2[find2(sta[i])]=find2(v);
				}
			}
			int rot=find2(tem[1]);
			// cout<<rot<<endl;
			for (int i=2; i<=usiz; ++i) if (find2(tem[i])!=rot) goto jump;
			if (__builtin_popcount(s)<ans) ans=__builtin_popcount(s), mini=s;
			jump: ;
		}
		printf("%d\n", ans+(usiz!=0));
		if (!(ans+(usiz!=0))) return ;
		for (int i=0; i<top; ++i) if (mini&(1<<i))
			for (auto it:pts[sta[i]]) s[it.fir][it.sec]=1;
		puts("+");
		for (int i=1; i<=n; ++i) {for (int j=1; j<=m; ++j) printf("%d", s[i][j]); printf("\n");}
		for (int i=0; i<top; ++i) if (mini&(1<<i)) {
			for (int j=1; j<=n; ++j) for (int k=1; k<=m; ++k) s[j][k]=0;
			for (auto it:pts[sta[i]]) s[it.fir][it.sec]=1;
			puts("-");
			for (int i=1; i<=n; ++i) {for (int j=1; j<=m; ++j) printf("%d", s[i][j]); printf("\n");}
		}
	}
}

void print() {
	int u=n, v=m;
	if (rev) {
		for (int i=1; i<=max(n, m); ++i)
			for (int j=i+1; j<=max(n, m); ++j)
				swap(s[i][j], s[j][i]);
		swap(u, v);
		for (int i=1; i<=u; ++i) {for (int j=1; j<=v; ++j) printf("%d", s[i][j]); printf("\n");}
	}
	else for (int i=1; i<=n; ++i) {for (int j=1; j<=m; ++j) printf("%d", s[i][j]); printf("\n");}
}

namespace task1{
	int tem[N], usiz;
	void solve() {
		memset(vis, 0, sizeof(vis));
		for (int i=1; i<=n; ++i)
			for (int j=1; j<=m; ++j)
				if (!vis[find(id[i][j])]) {
					if (s[i][j]) tem[++usiz]=find(id[i][j]);
					vis[find(id[i][j])]=1;
				}
		printf("%d\n", usiz);
		for (int i=1; i<=usiz; ++i) {
			for (int i=1; i<=n; ++i) for (int j=1; j<=m; ++j) s[i][j]=0;
			for (auto it:pts[tem[i]]) s[it.fir][it.sec]=1;
			puts("+");
			print();
		}
	}
}

namespace task{
	char bkp[N][N];
	int black[N*N], white[N*N], bcnt, wcnt;
	void solve() {
		memset(vis, 0, sizeof(vis));
		for (int i=1; i<=n; ++i)
			for (int j=1; j<=m; ++j)
				if (!vis[find(id[i][j])]) {
					if (!s[i][j]) white[++wcnt]=find(id[i][j]);
					else black[++bcnt]=find(id[i][j]);
					vis[find(id[i][j])]=1;
				}
		if (!bcnt) {puts("0"); return ;}
		if (bcnt==1) {
			puts("1");
			puts("+");
			print();
			return ;
		}
		for (int i=1; i<=wcnt; ++i) if (to[white[i]].size()==bcnt) {
			puts("2");
			for (auto it:pts[white[i]]) s[it.fir][it.sec]=1;
			puts("+");
			print();
			for (int i=1; i<=n; ++i) for (int j=1; j<=m; ++j) s[i][j]=0;
			puts("-");
			for (auto it:pts[white[i]]) s[it.fir][it.sec]=1;
			print();
			return ;
		}
		puts("3");
		for (int i=1; i<=n; ++i) for (int j=1; j<=m; ++j) bkp[i][j]=s[i][j];
		puts("+");
		for (int i=1; i<=n; ++i) for (int j=1; j<=m; ++j) s[i][j]=0;
		for (int i=1; i<=n; ++i) s[i][1]=1;
		for (int i=1; i<=n; i+=2) for (int j=1; j<m; ++j) s[i][j]=1;
		for (int i=2; i<=n; i+=2) for (int j=2; j<m; ++j) if (bkp[i][j]) s[i][j]=1;
		print();
		puts("+");
		for (int i=1; i<=n; ++i) for (int j=1; j<=m; ++j) s[i][j]=0;
		for (int i=1; i<=n; ++i) s[i][m]=1;
		for (int i=2; i<=n; i+=2) for (int j=2; j<=m; ++j) s[i][j]=1;
		for (int i=1; i<=n; i+=2) for (int j=2; j<m; ++j) if (bkp[i][j]) s[i][j]=1;
		print();
		puts("-");
		for (int i=1; i<=n; ++i) for (int j=1; j<=m; ++j) s[i][j]=1;
		for (int i=1; i<=n; ++i) if (bkp[i][1]) s[i][1]=0;
		for (int i=1; i<=n; ++i) if (bkp[i][m]) s[i][m]=0;
		print();
		return ;
	}
}

signed main()
{
	freopen("bitbit.in", "r", stdin);
	freopen("bitbit.out", "w", stdout);

	scanf("%d%d", &n, &m);
	for (int i=1; i<=n; ++i) {
		scanf("%s", s[i]+1);
		for (int j=1; j<=m; ++j) s[i][j]-='0';
	}
	if (n>m) {
		rev=1;
		for (int i=1; i<=max(n, m); ++i)
			for (int j=i+1; j<=max(n, m); ++j)
				swap(s[i][j], s[j][i]);
		swap(n, m);
	}
	for (int i=1; i<=n; ++i) for (int j=1; j<=m; ++j) id[i][j]=++tot, pts[tot].insert({i, j});
	for (int i=1; i<=tot; ++i) dsu[i]=i;
	for (int i=1; i<=n; ++i)
		for (int j=1; j<=m; ++j)
			for (int k=0; k<4; ++k) {
				int x=i+dlt[k][0], y=j+dlt[k][1];
				if (x>=1&&x<=n&&y>=1&&y<=m&&s[x][y]==s[i][j]) uni(id[i][j], id[x][y]);
			}
	for (int i=1; i<=n; ++i)
		for (int j=1; j<=m; ++j)
			for (int k=0; k<4; ++k) {
				int x=i+dlt[k][0], y=j+dlt[k][1];
				if (x>=1&&x<=n&&y>=1&&y<=m&&s[x][y]!=s[i][j]) to[find(id[i][j])][find(id[x][y])]=1;
			}
	for (int i=1; i<=n; ++i) for (int j=1; j<=m; ++j) if (s[i][j]&&!vis[find(id[i][j])]) ++cnt, vis[find(id[i][j])]=1;
	// cout<<"cnt: "<<cnt<<endl;
	// force::solve();
	if (n==1) task1::solve();
	else task::solve();

	return 0;
}
posted @ 2022-03-22 17:24  Administrator-09  阅读(2)  评论(0编辑  收藏  举报