[20220322联考] 启程的日子

前言

构造题× 智商检测题√

题目

没有链接

给一个 \(n\times m\) 的 01 矩阵表示目标状态,你一次操作可以加/减一个 \(n\times m\) 的 01 矩阵,问最少多少次可以达到目标状态,并给出构造方案。

这里的加/减指的是对应位置的整数相加减,运算过程不要求值的范围在 \([0,1]\)

\(1\le n,m\le 500.\)

讲解

首先我们特判掉各种特殊情况(\(n=1\)\(m=1\),答案为 \(0,1,2\))。

考虑一种平凡情况,\(\min\{n,m\}>1\),那么答案为 \(3\),接下来讲构造方法。

首先我们用两步 \(+\) 操作构造出这样类似于两把刷子的图形,恰好覆盖每个点 1 次。

然后我们把目标状态是 0 的点在图上标出来:

第三步就是把这些点用 \(-\) 操作减掉,所以我们需要把它们连起来:

绿色点是我们为了第三步四联通而强行减掉的点,它们本应为 1,所以我们要在前两步使它们加到 2。这下梳子模型的优势就体现出来了,可以在任意点加而依然保证四联通。

在具体实现时我们其实并不需要大费周章地去找绿色点使得红色点联通,我们只需要这样偷懒:

也就是把中间全部都点成绿色,但是这样在 \(n=2\)\(m=2\) 的时候不适用,所以也要特判。

时间复杂度是平方级别。

代码

长长长
//12252024832524
#include <bits/stdc++.h>
#define TT template<typename T>
using namespace std;

typedef long long LL;
const int MAXN = 505;
int n,m;
char a[MAXN][MAXN];

LL Read()
{
	LL x = 0,f = 1; char c = getchar();
	while(c > '9' || c < '0'){if(c == '-') f = -1;c = getchar();}
	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
	return x * f;
}
TT void Put1(T x)
{
	if(x > 9) Put1(x/10);
	putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
	if(x < 0) putchar('-'),x = -x;
	Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return x > y ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Abs(T x){return x < 0 ? -x : x;}

bool check0(){
	for(int i = 0;i < n;++ i)
		for(int j = 0;j < m;++ j)
			if(a[i][j] == '1') return 0;
	return 1;
}
int ID[MAXN][MAXN],f[MAXN*MAXN];
int findSet(int x){if(f[x] ^ x) f[x] = findSet(f[x]);return f[x];}
void unionSet(int x,int y){f[findSet(x)] = findSet(y);}
bool check1(){
	int nx = 0,ny = 0;
	for(int i = 0;i < n;++ i)
		for(int j = 0;j < m;++ j){
			ID[i][j] = i*m+j; f[ID[i][j]] = ID[i][j];
			if(i && a[i-1][j] == a[i][j]) unionSet(ID[i-1][j],ID[i][j]);
			if(j && a[i][j-1] == a[i][j]) unionSet(ID[i][j-1],ID[i][j]);
			if(a[i][j] == '1') nx = i,ny = j;
		}
	for(int i = 0;i < n;++ i)
		for(int j = 0;j < m;++ j)
			if(a[i][j] == '1' && findSet(ID[i][j]) != findSet(ID[nx][ny]))
				return 0;
	return 1;
}
void solve1(){
	Put(1,'\n');
	putchar('+');putchar('\n');
	for(int i = 0;i < n;++ i,putchar('\n'))
		for(int j = 0;j < m;++ j)
			putchar(a[i][j]);
}
int cnt[MAXN*MAXN],st[MAXN],dx[4] = {1,-1},dy[4] = {0,0,1,-1};
vector<int> ling[MAXN*MAXN];
bool check2(){
	int cnt1 = 0;
	for(int i = 0;i < n;++ i)
		for(int j = 0;j < m;++ j)
			if(a[i][j] == '1' && findSet(ID[i][j]) == ID[i][j])
				++cnt1;
	for(int i = 0;i < n;++ i)
		for(int j = 0;j < m;++ j)
			if(a[i][j] == '0')
				ling[findSet(ID[i][j])].emplace_back(ID[i][j]);
	int win = -1;
	for(int i = 0;i < n && win < 0;++ i)
		for(int j = 0;j < m && win < 0;++ j)
			if(ling[ID[i][j]].size()){
				int tl = 0;
				for(auto &A : ling[ID[i][j]]){
					int x = A/m,y = A%m;
					for(int k = 0;k < 4;++ k){
						int tox = x+dx[k],toy = y+dy[k];
						if(tox >= 0 && toy >= 0 && tox < n && toy < m && a[tox][toy] == '1')
							st[++tl] = findSet(ID[tox][toy]);
					}
				}
				if(!tl) continue;
				sort(st+1,st+tl+1);
				if(unique(st+1,st+tl+1)-st-1 == cnt1) win = ID[i][j];
			}
	if(win < 0) return 0;
	Put(2,'\n');
	putchar('+'); putchar('\n');
	for(int i = 0;i < n;++ i,putchar('\n'))
		for(int j = 0;j < m;++ j)
			if(findSet(ID[i][j]) == win || a[i][j] == '1') putchar('1');
			else putchar('0');
	putchar('-'); putchar('\n');
	for(int i = 0;i < n;++ i,putchar('\n'))
		for(int j = 0;j < m;++ j)
			if(findSet(ID[i][j]) == win) putchar('1');
			else putchar('0');
	return 1;
}
void solvenm1(){
	int ans = 0;
	if(n == 1){
		for(int i = 0;i < m;++ i)
			if(a[0][i] == '1' && (!i || a[0][i-1] == '0')) ++ans;
		Put(ans,'\n');
		for(int i = 0,j;i < m;i = j+1){
			j = i;
			if(a[0][i] == '0') continue;
			putchar('+'); putchar('\n');
			while(j < m-1 && a[0][j+1] == '1') ++j;
			for(int k = 0;k < m;++ k)
				if(i <= k && k <= j) putchar('1');
				else putchar('0');
			putchar('\n');
		}
	}
	else {
		for(int i = 0;i < n;++ i)
			if(a[i][0] == '1' && (!i || a[i-1][0] == '0')) ++ans;
		Put(ans,'\n');
		for(int i = 0,j;i < n;i = j+1){
			j = i;
			if(a[i][0] == '0') continue;
			putchar('+'); putchar('\n');
			while(j < n-1 && a[j+1][0] == '1') ++j;
			for(int k = 0;k < n;++ k)
				if(i <= k && k <= j) putchar('1'),putchar('\n');
				else putchar('0'),putchar('\n');
		}
	}
}
char pt[3][MAXN][MAXN];
int bl[MAXN][MAXN];
void solve(){
	for(int opt = 0;opt < 3;++ opt)
		for(int i = 0;i < n;++ i)
			for(int j = 0;j < m;++ j)
				pt[opt][i][j] = '0';
	for(int i = 0;i < n;++ i)
		for(int j = 0;j < m;++ j){
			if(!j || (!(i&1) && j < m-1)) bl[i][j] = 0;
			else bl[i][j] = 1;
			pt[bl[i][j]][i][j] = '1';
			if(a[i][j] == '0') pt[2][i][j] = '1';
		}
	if(n == 2 || m == 2){
		for(int i = 0;i < n;++ i)
			for(int j = 0;j < m;++ j)
				if(a[i][j] == '1')
					pt[bl[i][j]^1][i][j] = '1',pt[2][i][j] = '1';
	}
	else{
		for(int i = 0;i < n;++ i)
			for(int j = 1;j < m-1;++ j)
				if(a[i][j] == '1')
					pt[bl[i][j]^1][i][j] = '1',pt[2][i][j] = '1';
	}
	Put(3,'\n');
	for(int opt = 0;opt < 3;++ opt){
		if(opt <= 1) putchar('+');
		else putchar('-');
		putchar('\n');
		for(int i = 0;i < n;++ i,putchar('\n'))
			for(int j = 0;j < m;++ j)
				putchar(pt[opt][i][j]);
	}
}

int main()
{
//	freopen("bitbit.in","r",stdin);
//	freopen("bitbit.out","w",stdout);
	n = Read(); m = Read();
	for(int i = 0;i < n;++ i) scanf("%s",a[i]);	
	if(check0()) {Put(0,'\n');return 0;}
	if(check1()) {solve1();return 0;}
	if(check2()) return 0;
	if(n == 1 || m == 1) {solvenm1();return 0;}
	solve();
	return 0;
}
posted @ 2022-03-22 17:22  皮皮刘  阅读(79)  评论(1编辑  收藏  举报