题目描述
有一个(n*m)的地图,把左边界和右边界粘起来使得形成一个圆柱,现在要不断地挖去其中的格子,要求任何时候都存在一条从最上方到最下方的路径(四联通),如果某次操作不满足要求则不做,问最后有多少次操作是成功的。
题解
从上到下四联通可以转化成从左到右删去的点不能够八联通,考虑怎么判断是否八联通。
首先我们将地图复制一遍,左边一份,右边一份,每次在左边和右边依次加入删去的点,然后我们对于删去的点进行维护。
我们使用并查集维护联通块,每个点的(fa)代表它所在的联通块中删去最早的点。
如果加入的两个点所在的联通块相同的话,由于这两个点本质上又是一个点,所以就形成了从左到右的八联通通道。
注意:额外判断(m=1)时,加入任何一个点肯定会导致从左到右删去的点能够八联通。
#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
const int N = 6e5 + 5;
int n, m, Q, ans, fa[N], cnt, vis[3005][6005], pos[N];
int nx[10] = {0, 0, 0, 1, 1, 1, -1, -1, -1};
int ny[10] = {0, 1, -1, 0, 1, -1, 0, 1, -1};
inline int read()
{
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
int find(int x) {return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);}
int way(int x, int y)
{
if(m == 1) return 0; int u, v; cnt ++;
for(int k = 1; k <= 2; k ++)
{
if(k == 1) u = x, v = y; else u = x, v = y + m;
for(int i = 1; i <= 8; i ++)
{
int uu = u + nx[i], vv = v + ny[i];
if(uu < 1 || uu > n) continue;
if(vv > 2 * m) vv -= 2 * m; else if(vv <= 0) vv += 2 * m;
if(vis[uu][vv])
{
if(k == 1) pos[find(vis[uu][vv])] = cnt;
else if(pos[find(vis[uu][vv])] == cnt) return 0;
}
}
}
return 1;
}
int main()
{
n = read(); m = read(); Q = read(); int x, y;
for(int i = 1; i <= 2 * Q; i ++) fa[i] = i;
for(int num = 1, u, v; num <= Q; num ++)
{
x = read(); y = read();
if(way(x, y))
{
vis[x][y] = num; vis[x][y + m] = num + Q;
for(int k = 1; k <= 2; k ++)
{
if(k == 1) u = x, v = y; else u = x, v = y + m;
for(int i = 1; i <= 8; i ++)
{
int uu = u + nx[i], vv = v + ny[i];
if(uu < 1 || uu > n) continue;
if(vv > 2 * m) vv -= 2 * m; else if(vv <= 0) vv += 2 * m;
if(vis[uu][vv]) fa[find(vis[u][v])] = find(vis[uu][vv]);
}
}
ans ++;
}
}
printf("%d
", ans);
return 0;
}