设备塔

设 备 塔 设备塔

题目链接:jzoj 3809

题目

为了封印辉之环,古代塞姆利亚大陆的人民在异空间中建造了一座设备塔。
简单的说,这座设备塔是一个漂浮在异空间中的圆柱体,圆柱体两头的圆是计算核心,而侧面则是
传输信息所用的数据通道,划分成 N ∗ m N*m Nm 个区块。
然而,随着工作的继续进行,他们希望把侧面的一部分区块也改造成其他模块。然而,任何时候都
必须保证存在一条数据通道,能从圆柱体的一端通向另一端。
由于无法使用辉之环掌控下的计算系统,他们寻求你的帮助来解决这个问题。他们将逐个输入想要
改造的区域,而你则执行所有可行的改造并忽略可能导致数据中断的改造。

输入

第一行,包含两个整数 N N N; M M M; K K K,表示侧面的长和宽,以及操作数。
接下来 K K K 行,每行包含三个整数 x i xi xi; y i yi yi,表示操作的区块的坐标。 ( 0 < = y = < M ) (0<=y=<M) (0<=y=<M)
数据保证不会对已经操作成功的区块进行操作。

输出

输出一行,表示有多少个操作可以被执行。

样例输入

3 4 9
2 2
3 2
2 3
3 4
3 1
1 3
2 1
1 1
1 4

样例输出

6

数据范围

对于分值为 30 30 30的子任务 1 1 1,保证 N N N; M < = 100 M <=100 M<=100; K < = 5000 K<= 5000 K<=5000
对于分值为 30 30 30的子任务 2 2 2,保证 N N N; M < = 3000 M <=3000 M<=3000; K < = 5000 K <= 5000 K<=5000
对于分值为 40 40 40的子任务 3 3 3,保证 N N N; M < = 3000 M <=3000 M<=3000; K < = 300000 K <= 300000 K<=300000

思路

这道题我们用并查集来做。
这种题一看到环,我们就会把它在复制一遍。

那么我们可以看如果把已经可以执行的地方连城线,如果一个点和它复制一遍的对应点可以连起来的话,通道就没了。
(可以自行想象了为什么,一个点的八个方向都可以连线)

然后对于怎么看两个点是否连通,我们可以枚举这两个点旁边的点,看看它们旁边的点能不能连通。因为如果这两个点连通,这两个点有分别和我们要看的两个点连通,那么我们要看的两个点也就肯定连通。

代码

#include<cstdio>

using namespace std;

int n, m, k, x, y, a[3001][6001], fa[6000001], sum;
int dx[8] = {1, 1, 1, 0, 0, -1, -1, -1}, dy[8] = {1, 0, -1, 1, -1, 1, 0, -1};

int find(int x) {//并查集找祖先
	if (fa[x] == x) return x;
	return fa[x] = find(fa[x]);
}

void connect(int x, int y) {//并查集链接
	int aa = find(x), bb = find(y);
	if (aa < bb) fa[bb] = aa;
		else if (aa > bb) fa[aa] = bb;
}

bool ch(int x, int &y) {//判断是否能连并处理出界问题
	if (x < 1 || x > n) return 0;
	if (y < 1) y = 2 * m;
	if (y > 2 * m) y = 1;
	if (!a[x][y]) return 0;
	return 1;
}

bool work(int x, int y) {
	int xa = x, ya = y + m;//展开后的另一个点
	for (int i = 0; i < 8; i++) {
		int xx = x + dx[i], xy = y + dy[i];//第一个点找连接的
		if (!ch(xx, xy)) continue;
		for (int j = 0; j < 8; j++) {
			int yx = xa + dx[j], yy = ya + dy[j];//第二个点找连接的
			if (!ch(yx, yy)) continue;
			if (find(a[xx][xy]) == find(a[yx][yy]) && a[xx][xy] && a[yx][yy]) return 0;//是否连通
		}
	}
	return 1;
}

void findfather(int x, int y) {//找到附近的点
	a[x][y] = ++sum;//记录
	fa[sum] = sum;//初始化
	for (int i = 0; i < 8; i++) {
		int ax = x + dx[i], ay = y + dy[i];
		if (!ch(ax, ay)) continue;
		connect(a[x][y], a[ax][ay]);//与附近的点连接
	}
}

int main() {
	scanf("%d %d %d", &n, &m, &k);//输入
	
	for (int hh = 1; hh <= k; hh++) {
		scanf("%d %d", &x, &y);//输入
		if (work(x, y)) {//可以执行操作
			findfather(x, y);//连点
			findfather(x, y + m);//连点
		}
	}
	
	printf("%d", sum / 2);//输出可以的次数
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值