【题解】P4898 [IOI2018] seats 排座位

发布时间 2023-04-07 21:58:20作者: kymru

思路

线段树。

题意可以转化成每次判定有多少个前缀满足所有结点构成矩形。

首先排除确定矩阵坐标再数答案的做法,因为太难。

所以考虑如何对前缀进行判定。

一个简单的想法是维护前 \(i\) 个点中 \(x, y\) 坐标的最值,但这样只能暴力看矩阵中的所有元素,跑得很慢。

不妨思考一下合法的条件:

  • \(i\) 个点都可以被一个矩形覆盖,这个恒成立。

  • 这个矩形内没有其他的点。

对于 2,考虑把前 \(i\) 个点和剩下的点分开,考虑把前 \(i\) 个染成黑色,后面的染成白色。

于是现在可以考虑黑白分布情况:

  • 所有的黑点必须连通,等价于左边的相邻点和上边的相邻点都是白点的黑点只有 \(1\) 个。

  • 矩形内没有白点,也就是说黑点不会有 L 形的分布,等价于不存在相邻点中有超过一个黑点的白点。

结论不会证,但是是对的,差点猜出来。

不太好维护每个时刻图内的状况。

换个角度,既然每次交换至多影响 \(10\) 个点的状态,那直接维护每个点对答案的贡献,每次只需要 \(O(\log)\) 计算贡献就能做。

先考虑第一个条件。如果点 \((x, y)\) 在时刻 \(i\) 是合法的,那么显然有 \(t_{x, y} \leq i < \min(t_{x - 1, y}, t_{x, y - 1})\).

第二个条件同理有 \(\operatorname{SecondMin(t_{x - 1, y}, t_{x, y - 1}, t_{x + 1, y}, t_{x, y + 1})} \leq i < t_{x, y}\).

于是一个点可以贡献的时刻都是确定的,直接对于点动态算贡献就好了。

注意四连通的点处理出来之后要去重,不然就会喜提 56pts.

代码

#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;

typedef pair<int, int> pii;
#define il inline
#define fi first
#define se second
#define mp make_pair

const int maxn = 1e6 + 5;
const int sgt_sz = maxn << 2;
const int inf = 2e9;

int h, w, q, n;
int tmp[4];
int r[maxn], c[maxn];
int dx[5] = {0, -1, 1, 0, 0}, dy[5] = {0, 0, 0, -1, 1};
pii que[10];
vector<int> a[maxn];

il int read()
{
	int res = 0;
	char ch = getchar();
	while ((ch < '0') || (ch > '9')) ch = getchar();
	while ((ch >= '0') && (ch <= '9')) res = res * 10 + ch - '0', ch = getchar();
	return res;
}

il void write(int x)
{
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}

il void write(int x, char ch) { write(x), putchar(ch); }

namespace SGT
{
	#define ls (k << 1)
	#define rs (k << 1 | 1)

	int lazy[sgt_sz];

	struct node
	{
		int v[2], c[2];

		node() { v[0] = v[1] = c[0] = c[1] = 0; }

		friend node operator + (node a, node b)
		{
			node res;
			res.v[0] = min(a.v[0], b.v[0]);
			res.v[1] = min(max(a.v[0], b.v[0]), min(a.v[1], b.v[1]));
			if (res.v[0] == res.v[1]) res.v[1] = inf;
			for (int i = 0; i < 2; i++)
				for (int j = 0; j < 2; j++)
				{
					if (res.v[i] == a.v[j]) res.c[i] += a.c[j];
					if (res.v[i] == b.v[j]) res.c[i] += b.c[j];
				}
			return res;
		}
	} tr[sgt_sz];

	il void push_up(int k) { tr[k] = tr[ls] + tr[rs]; }

	il void push_down(int k)
	{
		tr[ls].v[0] += lazy[k], tr[ls].v[1] += lazy[k], lazy[ls] += lazy[k];
		tr[rs].v[0] += lazy[k], tr[rs].v[1] += lazy[k], lazy[rs] += lazy[k];
		lazy[k] = 0;
	}

	il void build(int k, int l, int r)
	{
		lazy[k] = 0, tr[k].v[1] = inf, tr[k].c[0] = r - l + 1;
		if (l == r) return;
		int mid = (l + r) >> 1;
		build(ls, l, mid), build(rs, mid + 1, r);
	}

	il void update(int k, int l, int r, int ql, int qr, int w)
	{
		if (ql > qr) return;
		if ((l >= ql) && (r <= qr)) return tr[k].v[0] += w, tr[k].v[1] += w, lazy[k] += w, void();
		push_down(k);
		int mid = (l + r) >> 1;
		if (ql <= mid) update(ls, l, mid, ql, qr, w);
		if (qr > mid) update(rs, mid + 1, r, ql, qr, w);
		push_up(k);
	}

	il int query()
	{
		if (tr[1].v[0] == 1) return tr[1].c[0];
		if (tr[1].v[1] == 1) return tr[1].c[1];
		return 0;
	}
}
using namespace SGT;

bool in(int x, int y) { return (x >= 1) && (x <= h) && (y >= 1) && (y <= w); }

int chk1(int x, int y)
{
	int res = n + 1;
	res = min(res, (x > 1 ? a[x - 1][y] : inf));
	res = min(res, (y > 1 ? a[x][y - 1] : inf));
	return res - 1;
}

int chk2(int x, int y)
{
	tmp[0] = (x > 1 ? a[x - 1][y] : inf), tmp[1] = (x < h ? a[x + 1][y] : inf);
	tmp[2] = (y > 1 ? a[x][y - 1] : inf), tmp[3] = (y < w ? a[x][y + 1] : inf);
	sort(tmp, tmp + 4);
	return tmp[1];
}

int main()
{
	h = read(), w = read(), q = read(), n = h * w;
	for (int i = 1; i <= h; i++) a[i].resize(w + 1);
	for (int i = 1; i <= n; i++)
	{
		r[i] = read() + 1, c[i] = read() + 1;
		a[r[i]][c[i]] = i;
	}
	build(1, 1, n);
	for (int i = 1; i <= h; i++)
		for (int j = 1; j <= w; j++)
			update(1, 1, n, a[i][j], chk1(i, j), 1), update(1, 1, n, chk2(i, j), a[i][j] - 1, 1);
	while (q--)
	{
		int u = read() + 1, v = read() + 1, len = 0;
		for (int i = 0, x, y, tx, ty; i < 5; i++)
		{
			x = r[u], y = c[u], tx = x + dx[i], ty = y + dy[i];
			if (in(tx, ty)) que[++len] = mp(tx, ty);
			x = r[v], y = c[v], tx = x + dx[i], ty = y + dy[i];
			if (in(tx, ty)) que[++len] = mp(tx, ty);
		}
		sort(que + 1, que + len + 1);
		len = unique(que + 1, que + len + 1) - que - 1;
		for (int i = 1, tx, ty; i <= len; i++)
		{
			tx = que[i].fi, ty = que[i].se;
			update(1, 1, n, a[tx][ty], chk1(tx, ty), -1), update(1, 1, n, chk2(tx, ty), a[tx][ty] - 1, -1);
		}
		swap(r[u], r[v]), swap(c[u], c[v]), swap(a[r[u]][c[u]], a[r[v]][c[v]]);
		for (int i = 1, tx, ty; i <= len; i++)
		{
			tx = que[i].fi, ty = que[i].se;
			update(1, 1, n, a[tx][ty], chk1(tx, ty), 1), update(1, 1, n, chk2(tx, ty), a[tx][ty] - 1, 1);
		}
		write(query(), '\n');
	}
	return 0;
}