题解 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;
}
posted @ 2025-03-28 16:18  caijianhong  阅读(68)  评论(0)    收藏  举报