[ARC119D]Grid Repainting 3

89 篇文章 0 订阅

题目

传送门 to AtCoder

题意概要
有一个 n × m n\times m n×m 的棋盘,有些格子是红色,另外的是蓝色。每次选择一个红色格子,设其位于第 x x x 行第 y y y 列,则你可以选择二者之一:将它所在的这一行变为白色;将它所在的这一列变为白色。注意红色格子也会被染成白色,也包括被选中的这个格子。

求一个方案使得结束时白色格子最多。

数据范围与提示
n m ≤ 7 × 1 0 6 nm\le 7\times 10^6 nm7×106 。为了方便你定义数组, max ⁡ ( n , m ) ≤ 2500 \max(n,m)\le 2500 max(n,m)2500

思路

假设我们选择了一个红色格子,要消灭它这一列。那么我们肯定会希望,在此之前,所有这一列上的红色格子都用于消灭它所在的行。进一步的,所有在这些行上的红格子,又要去消灭这一列……大概就像这样。

在这里插入图片描述
我们希望使用左上角那个点来消灭这一列。那么它下面这些点就应该提前去消灭那一行。就像水波一样传开了。不难发现,每个点,只要被波及到,它所在的行和列都会被消灭。除了最特殊的——最开始那一个点的那一行,因为不能有红格子提前消灭这一行,把这个红格子给过早擦掉了。

也没有什么特殊情况。一个点有可能试着去 “激发” 一个已经被 “激发” 过的点,没关系。只要出现这种情况(譬如上图中,靠下的四个点),就随意选择一个顺序,最终的效果都是一样的。

所以,我们假想同一行、同一列的红格子之间存在无向边,那么 每个连通块恰有一行或一列会被浪费,其余都是可行的。

然后嘛,白格子的数量 = = = 总格子数量 − - ( n − a ) ( m − b ) (n-a)(m-b) (na)(mb),其中 a a a 是有多少个行被涂白, b b b 是有多少个列被涂白。由于我们并不在乎究竟是哪几行,所以我们直接枚举 t o t tot tot 个连通块中有多少个连通块丢失了行就行。

然后是构造方案。就按照我上面说的这样做就行了——每个连通块都不断地 “激发” 别人,除了起点的那一行或列是被禁止的。完了。其实挺简单的哈?

时间复杂度 O ( n m ) \mathcal O(nm) O(nm)

代码

#include <cstdio>
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long int_;
inline int_ readint(){
	int_ a = 0; char c = getchar(), f = 1;
	for(; c<'0'||c>'9'; c=getchar())
		if(c == '-') f = -f;
	for(; '0'<=c&&c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}
inline void writeint(int_ x){
	if(x > 9) writeint(x/10);
	putchar((x-x/10*10)^48);
}

const int MaxN = 2500;
char maze[MaxN][MaxN+5];
int n, m;

bool row[MaxN], col[MaxN];
int dfs(int x,int y){
	bool rx = row[x], cy = col[y];
	row[x] = col[y] = true;
	int res = 0; // how many taps
	if(!rx) rep(j,0,m-1)
		if(maze[x][j] == 'R')
			res += dfs(x,j);
	if(!cy) rep(i,0,n-1)
		if(maze[i][y] == 'R')
			res += dfs(i,y);
	if(rx != cy) ++ res;
	return res;
}

vector<int> rt;
int forbid_x, forbid_y;
void print(int x,int y){
	bool rx = row[x], cy = col[y];
	row[x] = col[y] = true;
	if(!rx) rep(j,0,m-1)
		if(maze[x][j] == 'R')
			print(x,j);
	if(!cy) rep(i,0,n-1)
		if(maze[i][y] == 'R')
			print(i,y);
	if(x == forbid_x) rx = true;
	if(y == forbid_y) cy = true;
	if(!rx && cy) printf("X %d %d\n",x+1,y+1);
	if(rx && !cy) printf("Y %d %d\n",x+1,y+1);
}

int main(){
	n = readint(), m = readint();
	rep(i,0,n-1)
		scanf("%s",maze[i]);
	int tot = 0, a = 0, b = 0;
	int calc = 0; // how many operations
	rep(i,0,n-1) rep(j,0,m-1)
		if(maze[i][j] == 'R' && !row[i] && !col[j]){
			calc += dfs(i,j), ++ tot; // new root
			rt.push_back(i*m+j);
		}
	rep(i,0,n-1) a += row[i]; // how many line
	rep(j,0,m-1) b += col[j]; // how many column
	int ans = 0, ansId = 0;
	for(int i=0; i<=tot; ++i){ // i roots choose to only go vertically
		int x = n-a+i, y = m-b+(tot-i);
		if(ans < n*m-x*y) // cnt_white
			ans = n*m-x*y, ansId = i;
	}
	memset(row,0,n), memset(col,0,m);
	printf("%d\n",calc+tot);
	for(int i=0; i<ansId; ++i){
		forbid_x = rt[i]/m, forbid_y = -1;
		print(rt[i]/m,rt[i]%m);
	}
	for(int i=ansId; i<tot; ++i){
		forbid_x = -1, forbid_y = rt[i]%m;
		print(rt[i]/m,rt[i]%m);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值