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