【LOJ2864】「IOI2018」排座位

本文介绍了LOJ2864题目,探讨了如何在网格图中根据1到t的染色规则判断是否形成矩形的条件,并提出使用线段树解决时间复杂度为O(HW+QLogHW)的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【题目链接】

【思路要点】

  • 对于一个 t t t ,将 1 , 2 , 3 , . . . , t 1,2,3,...,t 1,2,3,...,t 所在方格染黑,剩余方格染白,考虑网格图中所有的 2 ∗ 2 2*2 22 的正方形,那么 1 , 2 , 3 , . . . , t 1,2,3,...,t 1,2,3,...,t 形成一个矩形的充要条件为:
    1 1 1 、包含 1 1 1 个黑色方格 2 ∗ 2 2*2 22 的正方形恰好有 4 4 4 个。
    2 2 2 、不存在包含 3 3 3 个黑色方格 2 ∗ 2 2*2 22 的正方形。
  • 同时,当网格图中存在黑色方格时,包含 1 1 1 个黑色方格 2 ∗ 2 2*2 22 的正方形至少有 4 4 4 个,因此,我们只需要用线段树维护所有 t t t 对应的包含 1 / 3 1/3 1/3 个黑色方格 2 ∗ 2 2*2 22 的正方形的个数,并维护区间最小值的个数即可。
  • 时间复杂度 O ( H W + Q L o g H W ) O(HW+QLogHW) O(HW+QLogHW)

【代码】

#include "seats.h"
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e6 + 5;
int n, m, x[MAXN], y[MAXN];
vector <int> a[MAXN];
struct info {int one, tri; };
bool operator < (info a, info b) {
	if (a.one == b.one) return a.tri < b.tri;
	else return a.one < b.one;
}
info operator + (info a, info b) {
	return (info) {a.one + b.one, a.tri + b.tri};
}
bool operator == (info a, info b) {
	return a.one == b.one && a.tri == b.tri;
}
bool valid(info a) {
	return a.one == 4 && a.tri == 0;
}
struct SegmentTree {
	struct Node {
		int lc, rc, cnt;
		info Min, tag;
	} a[MAXN * 2];
	int n, size, root;
	void update(int root) {
		a[root].Min = min(a[a[root].lc].Min, a[a[root].rc].Min), a[root].cnt = 0;
		if (a[root].Min == a[a[root].lc].Min) a[root].cnt += a[a[root].lc].cnt;
		if (a[root].Min == a[a[root].rc].Min) a[root].cnt += a[a[root].rc].cnt;
	}
	void build(int &root, int l, int r, int *one, int *tri) {
		root = ++size;
		if (l == r) {
			a[root].Min = (info) {one[l], tri[l]};
			a[root].cnt = 1;
			return;
		}
		int mid = (l + r) / 2;
		build(a[root].lc, l, mid, one, tri);
		build(a[root].rc, mid + 1, r, one, tri);
		update(root);
	}
	void init(int x, int *one, int *tri) {
		n = x;
		root = size = 0;
		build(root, 1, n, one, tri);
	}
	void pushdown(int root) {
		info tmp = a[root].tag;
		a[a[root].lc].tag = a[a[root].lc].tag + tmp;
		a[a[root].lc].Min = a[a[root].lc].Min + tmp;
		a[a[root].rc].tag = a[a[root].rc].tag + tmp;
		a[a[root].rc].Min = a[a[root].rc].Min + tmp;
		a[root].tag = (info) {0, 0};
	}
	void modify(int root, int l, int r, int ql, int qr, info d) {
		if (l == ql && r == qr) {
			a[root].tag = a[root].tag + d;
			a[root].Min = a[root].Min + d;
			return;
		}
		pushdown(root);
		int mid = (l + r) / 2;
		if (mid >= ql) modify(a[root].lc, l, mid, ql, min(qr, mid), d);
		if (mid + 1 <= qr) modify(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, d);
		update(root);
	}
	void modify(int l, int r, info d) {
		if (l > r) return;
		else modify(root, 1, n, l, r, d);
	}
	int query() {
		if (valid(a[root].Min)) return a[root].cnt;
		else return 0;
	}
} ST;
int tri[MAXN], one[MAXN];
void give_initial_chart(int H, int W, vector <int> R, vector <int> C) {
	n = H, m = W;
	for (int i = 0; i <= n + 1; i++) {
		a[i].resize(m + 2);
		for (int j = 0; j <= m + 1; j++)
			a[i][j] = n * m + 1;
	}
	for (int i = 1; i <= n * m; i++) {
		x[i] = R[i - 1] + 1;
		y[i] = C[i - 1] + 1;
		a[x[i]][y[i]] = i;
	}
	for (int i = 0; i <= n; i++)
	for (int j = 0; j <= m; j++) {
		int tmp[4] = {a[i][j], a[i + 1][j], a[i][j + 1], a[i + 1][j + 1]};
		sort(tmp, tmp + 4);
		one[tmp[0]]++, one[tmp[1]]--;
		tri[tmp[2]]++, tri[tmp[3]]--;
	}
	for (int i = 1; i <= n * m; i++) {
		one[i] += one[i - 1];
		tri[i] += tri[i - 1];
	}
	ST.init(n * m, one, tri);
}
void update(int x, int y, int f) {
	int tmp[4] = {a[x][y], a[x + 1][y], a[x][y + 1], a[x + 1][y + 1]};
	sort(tmp, tmp + 4);
	ST.modify(tmp[0], tmp[1] - 1, (info) {f, 0});
	ST.modify(tmp[2], tmp[3] - 1, (info) {0, f});
}
void modify(int x, int y, int val) {
	update(x - 1, y - 1, -1);
	update(x, y - 1, -1);
	update(x - 1, y, -1);
	update(x, y, -1);
	a[x][y] = val;
	update(x - 1, y - 1, 1);
	update(x, y - 1, 1);
	update(x - 1, y, 1);
	update(x, y, 1);
}
int swap_seats(int p, int q) {
	p++, q++;
	int val = a[x[p]][y[p]], vbl = a[x[q]][y[q]];
	swap(x[p], x[q]);
	swap(y[p], y[q]);
	modify(x[p], y[p], val);
	modify(x[q], y[q], vbl);
	return ST.query();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值