很多分类讨论的题,不对拍很容易被卡正确性。
Description
给一个 $n\times m$ 的网格图,删去其中 $c$ 个点,问再删去至少几个点可以使图上存在两个点不连通。
无法达到目的输出 $-1$。
$n,m\leq10^9,c\leq10^5$。
Solution
首先答案最大是 $2$,删除一个角落相邻的点就可以做到。
玩一玩,答案是 $-1$ 有两种情况:$n\times m-c<2$,或 $n\times m-c=2$ 且剩下的两个点连通。
答案是 $0$ 就是已经存在不连通的两个点。
如果我们把 $n\times m$ 的图建出来,那么答案是 $1$ 就是存在割点。
剩下的答案就是 $2$ 了。
现在问题是图太大,我们需要把这个图缩成 $O(c)$ 个点。
很容易想到周围一大圈都没有被删掉的点的点是没用的,那我们的思路就是把被删掉的点周围的点拉出来建图。
只把周围 $3\times3$ 的点拉出来是不对的,考虑下面这个:
...
..*
...
$(2,2)$ 会成为割点,但实际上它并不是。
所以要拉出来 $5\times5$ 的点,并且只有在 $3\times 3$ 范围内的割点才有效。
然后判断原图是否连通可以用如下的办法:对建出来的图搜连通块,然后把相邻的被删掉的点看作一个整体,如果一组被删掉的点周围四连通的点不属于同一连通块,那么原图就不连通。
最后判断有没有割点用 tarjan 就好了。
复杂度在建图上,如果用哈希表实现,复杂度可以看作 $O(24c)$。
Code
常数巨大,别学。
namespace solve
{
const int maxn = 1e6 + 10;
typedef pair<int, int> pii;
const int dx4[] = {0, 0, 1, -1};
const int dy4[] = {1, -1, 0, 0};
vector<int> e[maxn];
int n, m, c;
inline bool is_in(int x, int y) { return x >= 1 && x <= n && y >= 1 && y <= m; }
pii a[maxn], all[maxn];
int cnt;
struct hash_map
{
static const int mod = maxn + -3;
int hash(const pii &x) { return (1ll * x.first * m + x.second) % mod; }
struct node
{
pii x;
int val, nxt;
} a[maxn];
int hd[mod], tot;
int &operator[](const pii &x)
{
int pos = hash(x);
for (int p = hd[pos]; p; p = a[p].nxt)
{
if (x == a[p].x)
{
return a[p].val;
}
}
a[++tot] = {x, 0, hd[pos]}, hd[pos] = tot;
return a[tot].val;
}
int count(const pii &x)
{
int pos = hash(x);
for (int p = hd[pos]; p; p = a[p].nxt)
if (x == a[p].x)
return 1;
return 0;
}
void clear()
{
memset(hd, 0, sizeof(hd)), tot = 0;
}
} mp, mp2;
inline bool has(pii x) { return mp2.count(x); }
inline bool has(int x, int y) { return has(pii(x, y)); }
namespace no_solution
{
bool work()
{
if (1ll * n * m - c <= 1)
return 1;
if (1ll * n * m - c > 2)
return 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
if (!has(i, j))
{
for (int k = 0; k < 4; k++)
{
int nx = i + dx4[k], ny = j + dy4[k];
if (is_in(nx, ny) && !has(nx, ny))
return 1;
}
return 0;
}
return 1;
}
}
namespace ans_eq_zero
{
int father[maxn];
int find(int x) { return x == father[x] ? x : father[x] = find(father[x]); }
void uni(int x, int y) { father[find(x)] = find(y); }
int color[maxn], tot, ucolor[maxn];
void dfs(int x, int col)
{
color[x] = col;
for (int v : e[x])
if (!color[v])
dfs(v, col);
}
bool work()
{
for (int i = 1; i <= cnt; i++)
father[i] = i, color[i] = ucolor[i] = 0;
for (int i = 1; i <= cnt; i++)
if (!color[i])
tot++, dfs(i, tot);
// for (int i = 1; i <= cnt; i++)
// cout << color[i] << " ";
// cout << endl;
for (int i = 1; i <= c; i++)
{
for (int j = 0; j < 4; j++)
{
int x = a[i].first + dx4[j], y = a[i].second + dy4[j];
if (is_in(x, y) && has(x, y))
uni(i, mp2[pii(x, y)]);
}
}
for (int i = 1; i <= c; i++)
{
for (int j = 0; j < 4; j++)
{
int x = a[i].first + dx4[j], y = a[i].second + dy4[j];
if (is_in(x, y) && mp.count(pii(x, y)))
{
int pos = mp[pii(x, y)];
if (!ucolor[find(i)])
ucolor[find(i)] = color[pos];
else if (ucolor[find(i)] != color[pos])
{
// cout << i << " " << x << " " << y << endl;
return 1;
}
}
}
}
return 0;
}
}
int dfn[maxn], low[maxn], son[maxn], tot, cut, cntt;
vector<int> ans;
void tarjan(int x, int fa)
{
dfn[x] = low[x] = ++cntt, son[x] = 0, tot++;
for (int v : e[x])
{
if (!dfn[v])
{
son[x]++;
tarjan(v, x);
low[x] = min(low[x], low[v]);
if (fa != 0 && low[v] >= dfn[x])
cut++, ans.push_back(x);
}
else if (v != fa)
low[x] = min(low[x], dfn[v]);
}
if (fa == 0 && son[x] >= 2)
cut++, ans.push_back(x);
}
int ok[maxn];
void clear()
{
mp.clear(), mp2.clear();
for (int i = 1; i <= cnt; i++)
dfn[i] = low[i] = son[i] = 0, e[i].clear(), ok[i] = 0;
cnt = cntt = 0, tot = cut = 0;
ans.clear();
}
bool check()
{
for (int p : ans)
if (ok[p])
return 1;
return 0;
}
void main()
{
clear();
n = read(), m = read(), c = read();
for (int i = 1; i <= c; i++)
a[i].first = read(), a[i].second = read(), mp2[a[i]] = i;
if (no_solution::work())
{
puts("-1");
return;
}
for (int i = 1; i <= c; i++)
for (int dx = -2; dx <= 2; dx++)
for (int dy = -2; dy <= 2; dy++)
{
int x = a[i].first + dx, y = a[i].second + dy;
if (!is_in(x, y) || has(x, y))
continue;
if (mp.count(pii(x, y)))
{
ok[mp[pii(x, y)]] |= abs(dx) <= 1 && abs(dy) <= 1;
continue;
}
mp[pii(x, y)] = ++cnt, all[cnt] = pii(x, y);
ok[cnt] = (abs(dx) <= 1 && abs(dy) <= 1);
}
// for (int i = 1; i <= cnt; i++)
// {
// cout << i << " " << all[i].first << " " << all[i].second << endl;
// }
if (c == 0)
{
puts((n == 1 || m == 1) ? "1" : "2");
return;
}
for (int i = 1; i <= cnt; i++)
{
for (int j = 0; j < 4; j++)
{
int x = all[i].first + dx4[j], y = all[i].second + dy4[j];
if (is_in(x, y) && mp.count(pii(x, y)))
{
int w = mp[pii(x, y)];
e[i].push_back(w);
e[w].push_back(i);
}
}
}
if (ans_eq_zero::work())
{
puts("0");
return;
}
for (int i = 1; i <= cnt; i++)
if (!dfn[i])
tarjan(i, 0);
if ((cut && check()) || n == 1 || m == 1)
puts("1");
else
puts("2");
}
}