[IOI2018] seats 排座位

【算法分析】

  • 显然同样大小的子矩阵中,符合条件的最多一个
  • 考虑判断 [ 0... i − 1 ] [0...i-1] [0...i1]的数是否构成一个矩形:
  • [ 0... i − 1 ] [0...i-1] [0...i1]所在格点染成黑色,剩下的染成白色
  • 将边界以外的格点看作白色
  • 如果 [ 0... i − 1 ] [0...i-1] [0...i1]的数是否构成一个矩形,那么这 i i i个点中:
  • ( 1 ) (1) (1) “左上都是白点”的黑点数记为 x x x x = 1 x=1 x=1
  • ( 2 ) (2) (2) “上下左右4个点中,是黑点的大于等于2个”的白点记为 y y y y = 0 y=0 y=0
  • 满足 ( 1 ) (1) (1),就满足了这 i i i个点处于同一连通块
  • 满足 ( 2 ) (2) (2),就满足了这 i i i个点构成一个矩形,不满足则说明存在“拐角”,例如(下图中 B B B为满足 ( 2 ) (2) (2)的白点):
    W B WB WB
    W W WW WW
  • 如果只满足 ( 2 ) (2) (2),不满足 ( 1 ) (1) (1),可能构成多个矩形
  • 因为不存在 x = 0 x=0 x=0 y = 1 y=1 y=1的情况(实际上是没有 x = 0 x=0 x=0的情况)
  • 所以满足 x + y = 1 x+y=1 x+y=1即合法
  • 线段树维护对于每个 i i i x + y x+y x+y的值
  • 并且维护区间最小值及最小值个数(最小值不可能为0)
  • 对于每个点 u u u,一定是先白后黑
  • u u u为白点的最晚时刻为 l [ u ] l[u] l[u],为黑点的最早时刻为 r [ u ] r[u] r[u]
  • u u u周围 4 4 4个点的 r r r次小值 z z z(如果点越界, r = i n f r=inf r=inf
  • 它作为满足 ( 2 ) (2) (2)的点的时段即 [ z , l [ u ] ] [z,l[u]] [z,l[u]](注意特判 z > l [ u ] z>l[u] z>l[u]的情况)
  • 那么在线段树上给 [ z , l [ u ] ] + 1 [z,l[u]]+1 [z,l[u]]+1
  • u u u左上的 l l l的最小值为 t t t(如果点越界, l = H W − 1 l=HW-1 l=HW1
  • 它作为满足 ( 1 ) (1) (1)的点的时段即 [ r [ u ] , t ] [r[u],t] [r[u],t](注意特判 r [ u ] > t r[u]>t r[u]>t的情况)
  • 那么在线段树上给 [ r [ u ] , t ] + 1 [r[u],t]+1 [r[u],t]+1
  • 每次交换时,见掉两个点 a , b a,b a,b及他们周围 4 4 4个点(共 O ( 10 ) O(10) O(10)个点)在两点交换前的贡献,交换 a , b a,b a,b,加上这些点在两点交换后的贡献
  • 注意如果 10 10 10个点中有重复的,不可以重复加减贡献
  • 要记录矩阵中每个位置上的数是什么,以及每个数所在位置是什么

【参考程序】注:由于个人习惯,以下程序中编号为 [ 1... H W ] [1...HW] [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; 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值