题解 QOJ8106【Mosaic】
题解:QOJ8106 Mosaic
- 35th Petrozavodsk Programming Camp, Summer 2018
- Day 5: Warsaw U Contest, XVIII Open Cup named after E.V. Pankratiev Onsite, Sunday, August 26, 2018
- Problem E. Mosaic
题目描述
平面上有 \(n\) 个点。现在需要对每个点分别为左下角画一个正方形,要求这些正方形的并为一个矩形,正方形互相没有交,正方形的边长为正整数。输出方案或者判断无解。
\(n\leq 2000\)。
solution
特判 \(n=1\) 后,考虑这个矩形的右下角,它一定是由 \(y\) 最小的点中 \(x\) 最大的点(称为 1 号点)顶着的,显然除了这个点之外没有点能顶到右下角。
于是尝试确定右下角的位置,这由 1 号点正方形的边长决定。又考虑到如果这个正方形的上边界所在直线上下都有点,而直线上没有点,则没有其他正方形能和这个正方形切着,一定不合法。
所以 1 号点正方形的边长要么为某个 \(y_i-y_1\),要么是其他点拼成一个矩形,然后 1 号点正方形拼上这个矩形。只有 \(O(n)\) 种可能的情况。
枚举边长。然后从下往上、从右往左扫描,每个点决定边长的时候一定是尽可能顶满(否则没有其他点能完成它未完成的工作)。
抽象一下也就是 \(i\) 号点决定边长 \(r_i\) 后立即插入一条 \(x=x_i, y\in [y_i, y_i+r_i)\) 的竖线段,然后要求其他点形成的正方形不能碰到这条竖线段,最后再扫描线判断是否拼成了矩形,这样就充要了。
还要用 1 号点正方形的边长限制其它正方形不能超出 1 号点正方形右边界,也是插一条无限长的竖线段。如果 1 号点正方形的边长是上面说的那种未定的情况,则在 \(x_1\) 上插一条无限长的竖线段,这部分也是一样的。
然后就将这个问题差不多解决了。要写 std::set,或者树状数组代替都可以,复杂度 \(O(n^2\log n)\)。
code
#include <bits/stdc++.h>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr, ##__VA_ARGS__)
#else
#define endl "\n"
#define debug(...) void(0)
#endif
using LL = long long;
constexpr int N = 2010;
#define all(vec) vec.begin(), vec.end()
template <class T>
struct flower {
vector<T> vec;
flower& operator<<(const T& x) { vec.push_back(x); return *this; }
int build() {
sort(all(vec)), vec.erase(unique(all(vec)), vec.end());
return (int)vec.size();
}
int operator()(const T& x) const { return lower_bound(all(vec), x) - vec.begin() + 1; }
T operator[](int x) const { return vec[x - 1]; }
};
struct fenwick {
int t[N + 10];
void clear() { memset(t, 0, sizeof t); }
void add(int p, int x) { for (; p <= N; p += p & -p) t[p] += x; }
int query(int p) { int r = 0; for (; p >= 1; p -= p & -p) r += t[p]; return r; }
int binary(int k) {
int p = 0;
for (int j = 1 << __lg(N); j; j >>= 1) {
if (p + j <= N && k >= t[p + j]) k -= t[p += j];
}
return p;
}
int lower_bound(int p) { return binary(query(p - 1)) + 1; }
} tr;
struct dot {
int x, y, id, hx;
} a[N];
int n, fans[N], rad[N];
flower<int> hua;
bool solve(int r1) {
tr.clear();
int maxx = hua.vec.back();
if (r1 > 0 && maxx >= a[1].x + r1) return rad[0] = -1, false;
using node = pair<int, int>;
priority_queue<node, vector<node>, greater<node>> q;
auto insert = [&](int hx, int yr) { tr.add(hx, +1), q.emplace(yr, hx); };
if (r1 > 0) insert(a[1].hx, a[1].y + r1), insert((int)hua.vec.size() + 1, 2e9 + 10), rad[1] = r1;
else insert(a[1].hx, 2e9 + 10);
int maxy = r1 > 0 ? a[1].y + rad[1] : -1;
for (int i = 2; i <= n; i++) {
while (!q.empty() && q.top().first <= a[i].y) tr.add(q.top().second, -1), q.pop();
int res = tr.lower_bound(a[i].hx);
rad[i] = (res > (int)hua.vec.size() ? a[1].x + rad[1] : hua[res]) - a[i].x;
if (rad[i] <= 0) return rad[0] = -1, false;
insert(a[i].hx, a[i].y + rad[i]);
maxy = max(maxy, a[i].y + rad[i]);
}
if (r1 <= 0) rad[1] = maxy - a[1].y;
#ifdef LOCAL
debug("possible solution: [r1 = %d]\n", r1);
for (int i = 1; i <= n; i++) debug("(%d, %d).r = %d\n", a[i].x, a[i].y, rad[i]);
#endif
map<int, basic_string<int>> mp;
for (int i = 1; i <= n; i++) {
mp[a[i].x] += a[i].y;
mp[a[i].x] += -1 - a[i].y - rad[i];
mp[a[i].x + rad[i]] += -1 - a[i].y;
mp[a[i].x + rad[i]] += a[i].y + rad[i];
}
map<int, int> mp2, mp0 = {{a[1].y, 1}, {maxy, -1}};
for (auto e : mp) {
for (int x : e.second) {
if (x >= 0) mp2[x] += 1;
else mp2[x = -1 - x] -= 1;
if (mp2[x] == 0) mp2.erase(x);
}
if (e.first == mp.rbegin()->first ? !mp2.empty() : mp2 != mp0) return rad[0] = -1, false;
}
return rad[0] = 0, true;
}
int mian() {
cin >> n;
hua = {};
for (int i = 1; i <= n; i++) cin >> a[i].x >> a[i].y, a[i].id = i, hua << a[i].x;
hua.build();
for (int i = 1; i <= n; i++) a[i].hx = hua(a[i].x);
sort(a + 1, a + n + 1, [&](auto lhs, auto rhs) { return lhs.y != rhs.y ? lhs.y < rhs.y : lhs.x > rhs.x; });
if (n > 1) {
if (!solve(-1)) for (int i = 1; i <= n; i++) if (a[i].y != a[1].y && solve(a[i].y - a[1].y)) break;
} else rad[1] = 1, rad[0] = 0;
if (rad[0] >= 0) {
cout << "YES";
for (int i = 1; i <= n; i++) fans[a[i].id] = rad[i];
for (int i = 1; i <= n; i++) cout << " " << fans[i];
cout << endl;
} else {
cout << "NO" << endl;
}
return 0;
}
int main() {
#ifndef LOCAL
cin.tie(nullptr)->sync_with_stdio(false);
#endif
int t;
cin >> t;
while (t--) mian();
return 0;
}
本文来自博客园,作者:caijianhong,转载请注明原文链接:https://www.cnblogs.com/caijianhong/p/18798291
浙公网安备 33010602011771号