Solution
- 显然同样大小的子矩阵中,符合条件的最多一个
- 考虑判断\([0...i-1]\)的数是否构成一个矩形:
- 将\([0...i-1]\)所在格点染成黑色,剩下的染成白色
- 将边界以外的格点看作白色
- 如果\([0...i-1]\)的数是否构成一个矩形,那么这\(i\)个点中:
- \((1)\) “左上都是白点”的黑点数记为\(x\),\(x=1\)
- \((2)\) “上下左右4个点中,是黑点的大于等于2个”的白点记为\(y\),\(y=0\)
- 满足\((1)\),就满足了这\(i\)个点处于同一连通块
- 满足\((2)\),就满足了这\(i\)个点构成一个矩形,不满足则说明存在“拐角”,例如(下图中\(B\)为满足\((2)\)的白点):
\(WB\)
\(WW\) - 如果只满足\((2)\),不满足\((1)\),可能构成多个矩形
- 因为不存在\(x=0\)且\(y=1\)的情况(实际上是没有\(x=0\)的情况)
- 所以满足\(x+y=1\)即合法
- 线段树维护对于每个\(i\),\(x+y\)的值
- 并且维护区间最小值及最小值个数(最小值不可能为0)
- 对于每个点\(u\),一定是先白后黑
- 记\(u\)为白点的最晚时刻为\(l[u]\),为黑点的最早时刻为\(r[u]\)
- 记\(u\)周围\(4\)个点的\(r\)的次小值为\(z\)(如果点越界,\(r=inf\))
- 它作为满足\((2)\)的点的时段即\([z,l[u]]\)(注意特判\(z>l[u]\)的情况)
- 那么在线段树上给\([z,l[u]]+1\)
- 记\(u\)左上的\(l\)的最小值为\(t\)(如果点越界,\(l=HW-1\))
- 它作为满足\((1)\)的点的时段即\([r[u],t]\)(注意特判\(r[u]>t\)的情况)
- 那么在线段树上给\([r[u],t]+1\)
- 每次交换时,见掉两个点\(a,b\)及他们周围\(4\)个点(共\(O(10)\)个点)在两点交换前的贡献,交换\(a,b\),加上这些点在两点交换后的贡献
- 注意如果\(10\)个点中有重复的,不可以重复加减贡献
- 要记录矩阵中每个位置上的数是什么,以及每个数所在位置是什么
Code
注:由于个人习惯,以下程序中编号为\([1...HW]\)
#include <bits/stdc++.h>
using namespace std;
#define p2 p << 1
#define p3 p << 1 | 1
template <class t>
inline void read(t & res)
{
char ch;
while (ch = getchar(), !isdigit(ch));
res = ch ^ 48;
while (ch = getchar(), isdigit(ch))
res = res * 10 + (ch ^ 48);
}
const int e = 1e6 + 5, dx[] = {-1, 0, 1, 0, 0}, dy[] = {0, -1, 0, 1, 0}, inf = 0x3f3f3f3f;
struct point
{
int x, y;
}a[e];
int val[e * 4], cnt[e * 4], n, m, add[e * 4], q, tmp, id[e], pos[e], vis[e], now;
inline void collect(int p)
{
if (val[p2] == val[p3])
{
val[p] = val[p2];
cnt[p] = cnt[p2] + cnt[p3];
}
else if (val[p2] < val[p3])
{
val[p] = val[p2];
cnt[p] = cnt[p2];
}
else
{
val[p] = val[p3];
cnt[p] = cnt[p3];
}
}
inline void pushdown(int p)
{
if (add[p])
{
add[p2] += add[p];
add[p3] += add[p];
val[p2] += add[p];
val[p3] += add[p];
add[p] = 0;
}
}
inline void build(int l, int r, int p)
{
if (l == r)
{
cnt[p] = 1;
return;
}
int mid = l + r >> 1;
build(l, mid, p2);
build(mid + 1, r, p3);
collect(p);
}
inline void update(int l, int r, int s, int t, int v, int p)
{
if (l == s && r == t)
{
val[p] += v;
add[p] += v;
return;
}
pushdown(p);
int mid = l + r >> 1;
if (t <= mid) update(l, mid, s, t, v, p2);
else if (s > mid) update(mid + 1, r, s, t, v, p3);
else
{
update(l, mid, s, mid, v, p2);
update(mid + 1, r, mid + 1, t, v, p3);
}
collect(p);
}
inline void upt(int l, int r, int v)
{
l = max(l, 1);
r = min(r, tmp);
update(1, tmp, l, r, v, 1);
}
inline int get(int x, int y)
{
return (x - 1) * m + y;
}
inline void join(int x, int y, int opt)
{
vis[get(x, y)] = now;
int i, p[4];
for (i = 0; i < 4; i++)
{
int xx = dx[i] + x, yy = dy[i] + y;
if (xx < 1 || yy < 1 || xx > n || yy > m) p[i] = inf;
else p[i] = id[get(xx, yy)];
}
sort(p, p + 4);
int tim = id[get(x, y)] - 1;
if (p[1] <= tim) upt(p[1], tim, opt);
tim++;
for (i = 0; i < 2; i++)
{
int xx = dx[i] + x, yy = dy[i] + y;
if (xx < 1 || yy < 1 || xx > n || yy > m) p[i] = inf;
else p[i] = id[get(xx, yy)] - 1;
}
sort(p, p + 2);
if (tim <= p[0]) upt(tim, p[0], opt);
}
inline void change(int x, int y, int opt)
{
int i;
for (i = 0; i < 5; i++)
{
int xx = x + dx[i], yy = y + dy[i];
if (xx < 1 || yy < 1 || xx > n || yy > m || vis[get(xx, yy)] == now) continue;
join(xx, yy, opt);
}
}
int main()
{
read(n); read(m); read(q);
int i, u, v;
tmp = n * m;
for (i = 1; i <= tmp; i++)
{
read(a[i].x);
a[i].x++;
read(a[i].y);
a[i].y++;
pos[i] = get(a[i].x, a[i].y);
id[get(a[i].x, a[i].y)] = i;
}
build(1, tmp, 1);
for (i = 1; i <= tmp; i++) join(a[i].x, a[i].y, 1);
while (q--)
{
read(u);
read(v);
u++;
v++;
now++;
change(a[u].x, a[u].y, -1);
change(a[v].x, a[v].y, -1);
int idx = u, idy = v, px = pos[idx],
py = pos[idy];
id[px] = idy;
id[py] = idx;
pos[idx] = py;
pos[idy] = px;
now++;
change(a[u].x, a[u].y, 1);
change(a[v].x, a[v].y, 1);
swap(a[u].x, a[v].x);
swap(a[u].y, a[v].y);
printf("%d\n", cnt[1]);
}
return 0;
}