[IOI2018] seats 排座位(线段树)

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; 
}
原文地址:https://www.cnblogs.com/cyf32768/p/12196341.html