CF-Round #634-div3-F题

CF-Round #634-div3-F题

F. Robots on a Grid

传送门

这道题图论,关于基环内向树的,用到了倍增。

题目大意:给你一个矩阵,矩阵的每个格子都有颜色,‘0’代表黑色,‘1’代表白色。每个格子上都标有走的方向。‘L’, ‘R’, ‘D’, ‘U’;
现在可以在这些格子上放一些机器人,这些机器人会按照格子所给的方向走,要求在任何时候都不允许有两个机器人在一个格子上的情况。(也就是不管是初始位置还是开始走的时候)
问最多可以放多少个机器人,并且初始条件下在黑色格子的机器人最多有多少个。

本题思路:
首先我们要把输入处理下来,都转化为1维数组的形式,下标都从1开始。
我们思考:要求任何时候都不允许有两个机器人在同一个格子上的情况。
我们可以假设放置上去的机器人走了nm个格子,如果这些机器人在nm个格子之后的位置不是在同一个位置,就说明他们不会相遇了。因为nm个格子是所给的总共的格子,已经走完一遍了,下一遍就是循环了。
在n
m个格子中,相遇了就会一直相遇,没相遇就再也不会遇见。
所以我们可以处理nm一个循环的格子。
用nex[][]保存,nex[i][j]表示第i + 1次从j走到的目的地。(i从0开始,所以是i+1次)
如果不同开始位置的机器人的目的地相同,那么我们只能选取一个机器人,因为题目要求最多的黑色格子,所以我们尽可能选取从黑色格子出发的机器人。
之后我们要模拟 n * m 的过程,看看n * m步到达的目的地是哪里
因为n
m最大是1e6,这里需要用到倍增。
用倍增的话1e6的数据最多处理20次。(二进制的最大位数)
所以我们处理好20次的模拟过程。记录每一个格子的下一步是哪里
倍增思路就是从大到小。(因为从小到大可能出现“悔棋”现象,我在LCA里面有详细说明)
走n * m步进行分割,看看每一次最多走多少步。(步数分析需要二进制下考虑)
b[i] = 1表示当前i这个格子是从黑色格子走过来的。
w[i] = 1表示当前i这个格子是从白色格子走过来的。
从当前格子开始走n*m步走到哪里去,目的地to需要标记。标记要参考初始位置的格子颜色,为黑就标记黑色,为白就标记白色。
(这里有情况是初始位置不相同,目的地相同的情况,需要注意到)
之后我们再跑一遍,如果当前标记为黑色的格子,我们就black++,ans++。特别注意b[i] = w[i] = 0;都需要标记为0;因为有可能目的地是一个地方,初始位置不一样,我们只能选取其中的一种,我们黑色的格子优先选择。

好啦~解释完啦

对了,我们memset有tle了。。。。啊!!所以我直接在维护答案的时候直接初始化了。

代码部分:

#include <bits/stdc++.h>
#define mst(a, n) memset(a, n, sizeof(a))
using namespace std;
const int N = 1e6 + 10;

int n, m;
int color[N];
char s[N];
int nex[24][N];
int w[N], b[N];

int main()
{
	int t;
	cin >> t;
	while (t--)
	{
		scanf ("%d%d", &n, &m);
		for (int i = 1; i <= n; i++)
		{
			scanf ("%s", s + 1);
			for (int j = 1; j <= m; j++)
			{
				color[(i - 1) * m + j] = (s[j] == '1');
			}
		}
		for (int i = 1; i <= n; i++)
		{
			scanf ("%s", s + 1);
			for (int j = 1; j <= m; j++)
			{
				int to;
				if (s[j] == 'U')
				{
					to = (i - 2) * m + j;
				}
				else if (s[j] == 'D')
				{
					to = i * m + j;
				}
				else if (s[j] == 'R')
				{
					to = (i - 1) * m + j + 1;
				}
				else 
				{
					to = (i - 1) * m + j - 1;
				}
				nex[0][(i - 1) * m + j] = to;
			}
		}
		n *= m;
		for (int i = 1; i <= 20; i++)
		{
			for (int j = 1; j <= n; j++)
			{
				nex[i][j] = nex[i - 1][nex[i - 1][j]];
			}
		}
		for (int j = 1; j <= n; j++)
		{
			int to = j;
			for (int i = 20; ~i; i--)//此循环到达-1结束,-1取反为0 
			{
				if ((1 << i) & n)//走了nm步,用倍增从大往小 
				{ 
					to = nex[i][to];
				}
			}
			color[j] ? w[to] = 1 : b[to] = 1;
		}
		int ans = 0;
		int blac = 0;
		for (int i = 1; i <= n; i++)
		{
			if (b[i])
			{
				b[i] = w[i] = 0;
				blac++;
				ans++;
			}
			else if (w[i])
			{
				w[i] = 0;
				ans++;
			}
		}
		cout << ans << " " << blac << endl; 
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

娃娃酱斯密酱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值