ABC 432 D题's 题解
标签:模拟
前言
根据数据范围判断算法,整体思维锻炼
正文
格式化题目
现在有一个无限大小的网格,一开始矩阵 \([(0,X-1),(Y-1,0)]\) 为黑色 \((X,Y\le 10^8)\),其他地方均为白色,给出 \(N(\le14)\) 次操作,每次操作形式如下:
- \(X~a~b\) 表示将纵轴小于 \(a\) 的点整体向下平移 \(b\) 个单元格,纵轴大于等于 \(a\) 的点整体向上平移 \(b\) 个单元格
- \(Y~a~b\) 表示将横轴小于 \(a\) 的点整体像左平移 \(b\) 个单元格,横轴大于等于 \(a\) 的点整体向右平移 \(b\) 个单元格
问,最后黑色的单元格构成了几个联通块?(以四联通为准)
思路(题解)
发现 \(N\) 特别小,可以从这个地方入手,但是我们又发现不好拿现有的数据结构维护这个联通块信息,像这种情况(操作次数小,操作不好维护)就可以考虑是大模拟,怎么模拟呢?
关键发现:
- 操作后不可能会出现重叠的联通块
- 所有联通块均为长方形
证明(先只考虑第一个操作,因为经过旋转后能把第二个操作变成第一个操作):
- 每次操作是根据一个 \(X\) 轴上的一个分界线进行两个不同的操作,所以从这个分界线分开,其他的点都是平移,不会改变相对位置,所以永远都不会有重叠
- 根据第一个能够看出最多把一个矩形变成两个相错的矩形,所以形状也不会有改变
所以用一个结构体,维护每一个矩形的四个坐标,每次操作就把这个矩形按照正常的操作分成两个矩形(先不考虑连通块),最后最多得到 \(2^N\) 个矩形,由于 \(N\) 足够小,所以没有问题,之后再判断有没有有公共边的矩形,再用并查集合起来,就能够得到答案了。
时间复杂度: \(\Theta(2^{2N})\)
空间复杂度: \(\Theta(2^N)\)
代码
#include <cstdio>
#include <vector>
#include <algorithm>
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int MAXSZ = 1 << 20;
char ch, buf[MAXSZ], *p1, *p2;
#define ge() (p1 == p2 && (p2 = buf + fread(p1 = buf, 1, MAXSZ, stdin), p1 == p2) ? EOF : *p1++)
template <typename T_T>
inline void read(T_T &x) {
x = 0;
bool flag = true;
while (ch < '0' || '9' < ch) {
ch = ge();
if (ch == '-') flag = false;
}
while ('0' <= ch && ch <= '9') {
x = x * 10 + (ch ^ 48);
ch = ge();
}
if (!flag)
x = -x;
}
template <typename T_T>
inline void write(T_T x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x > 9) write(x / 10);
putchar(x % 10 | 48);
}
template <typename T_T>
inline void swap(T_T &x, T_T &y) {
x ^= y;
y ^= x;
x ^= y;
}
const int N = 1 << 15;
struct Node {
ll xa, ya, xb, yb;
};
ll X, Y, sz[N], ans[N];
std::vector <Node> k, tmp_k;
int n, fa[N], cnt;
int find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);}
void merge(int x, int y) {
x = find(x), y = find(y);
if (x == y) return ;
if (sz[x] > sz[y]) swap(x, y);
fa[x] = y;
sz[y] += sz[x];
}
bool check1(int i, int j) {return (k[i].xa <= k[j].xa && k[j].xa <= k[i].xb) && (k[i].yb == k[j].ya + 1 || k[j].yb == k[i].ya + 1);}
bool check2(int i, int j) {return (k[i].ya >= k[j].ya && k[j].ya >= k[i].yb) && (k[i].xa - 1 == k[j].xb || k[j].xa - 1 == k[i].xb);}
int main() {
read(n), read(X), read(Y);
k.emplace_back((Node){0, Y - 1, X - 1, 0});
while (n --) {
char ch = ge();
ll a, b; read(a), read(b);
tmp_k.clear();
for (auto k1 : k) {
if (ch == 'X') {
if (k1.xb < a) tmp_k.emplace_back((Node){k1.xa, k1.ya - b, k1.xb, k1.yb - b});
if (k1.xa >= a) tmp_k.emplace_back((Node){k1.xa, k1.ya + b, k1.xb, k1.yb + b});
if (k1.xa < a && k1.xb >= a) {
tmp_k.emplace_back((Node){k1.xa, k1.ya - b, a - 1, k1.yb - b});
tmp_k.emplace_back((Node){a, k1.ya + b, k1.xb, k1.yb + b});
}
}
else {
if (k1.ya < a) tmp_k.emplace_back((Node){k1.xa - b, k1.ya, k1.xb - b, k1.yb});
if (k1.yb >= a) tmp_k.emplace_back((Node){k1.xa + b, k1.ya, k1.xb + b, k1.yb});
if (k1.ya >= a && k1.yb < a) {
tmp_k.emplace_back((Node){k1.xa - b, a - 1, k1.xb - b, k1.yb});
tmp_k.emplace_back((Node){k1.xa + b, k1.ya, k1.xb + b, a});
}
}
}
k = tmp_k;
}
for (int i = 0; i < k.size(); i++) fa[i] = i, sz[i] = (k[i].xb - k[i].xa + 1) * (k[i].ya - k[i].yb + 1);
for (int i = 0; i < k.size(); i++)
for (int j = i + 1; j < k.size(); j++)
if (check1(i, j) || check2(i, j) || check1(j, i) || check2(j, i))
merge(i, j);
for (int i = 0; i < k.size(); i++)
if (fa[i] == i)
ans[++ cnt] = sz[i];
std::sort(ans + 1, ans + cnt + 1);
write(cnt), putchar('\n');
for (int i = 1; i <= cnt; i++)
write(ans[i]), putchar(' ');
putchar('\n');
return 0;
}

浙公网安备 33010602011771号