[AGC051B] Bowling 题解

发布时间 2023-09-01 15:11:40作者: User-Unauthorized

题意

有数个大头钉在二维平面上,有四个人从不同的角度观察它们,重叠的点视为一个,是否可能有一个人观察到的大头钉数量远多余其他人?

让我们把大头钉的位置简化为二维坐标上的点。四个人观察的角度如下:

  • A 从左往右观察。即所有 \(y\) 坐标相同的点是重叠的。
  • B 从左下往右上观察。即所有 \(x\) 坐标与 \(y\) 坐标相减的值相同的点是重叠的。
  • C 从下往上观察。即所有 \(x\) 坐标相同的点是重叠的。
  • D 从右下往左上观察。即所有 \(x\) 坐标与 \(y\) 坐标相加的值相同的点是重叠的。

A, B, C, D 观察到的大头针数量为 \(a, b, c, d\),你需要构造一组大头针的排布,满足以下一些条件:

  • \(d \geq 10 \times \max\{a, b, c\}\)
  • 大头钉的数量不多于 \(10^5\) 个。
  • 大头钉的坐标均为 \([0, 10^9]\) 内的整数。
  • 不存在两个坐标相同的大头钉。

没有输入,直接输出一组解。

题解

\(N = 10\),那么满足题目要求的点集一定可以划分为若干集合,其中每个集合内的点 \(y\) 坐标相同,且这些集合的平均大小不小于 \(N\);类似的,其也可以划分为若干集合,其中每个集合内的点 \(x\) 坐标相同,且这些集合的平均大小不小于 \(N\)

我们现在考虑观察者 A 和观察者 C,发现其只有横纵坐标上的限制,故可以取 \(N\) 条平行于横轴的直线和 \(N\) 条平行于纵轴的直线,取其交点集合。可以发现这样构造的点集一定符合上述集合划分要求。

下面考虑观察者 B,可以发现如果按 \(x\) 坐标与 \(y\) 坐标相减的值划分点集,那么点 \(\left(x, y\right)\) 和点 \(\left(x + d, y + d\right)\) 一定在同一集合中,所以取 \(N\) 个合适的 \(d\) 值,对于上述只考虑观察者 A 和观察者 C 所构造出的点集中的每个点 \(\left(x, y\right)\),将点 \(\left(x + d, y + d\right)\) 也放入最终答案集合。可以发现现在构造出的点集满足观察者 ABC 观察到的点均为不少于 \(N\) 个点重叠在一起的形成的点。

考虑最大化观察者 D 观察到的点的数量,即最大化 \(x\) 坐标与 \(y\) 坐标相加的值的数量,随机取上文中的两组直线和 \(N\)\(d\) 值即可,由于值域很大,所以很少会有冲突的情况。最终答案点的数量为 \(N^3\),可以通过本题。

Code

#include <bits/stdc++.h>

typedef int valueType;
typedef std::vector<valueType> ValueVector;

void print(valueType x, valueType y) {
    std::cout << x << ' ' << y << '\n';
}

typedef std::mt19937 Engine;
typedef std::uniform_int_distribution<valueType> Distribution;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);

    constexpr valueType N = 40, V = 5e8;

    const unsigned int seed = (unsigned int) std::chrono::system_clock::now().time_since_epoch().count();

    std::cout << N * N * N << '\n';

    Engine engine(seed);
    Distribution distribution(0, V);

    ValueVector X(N), Y(N), D(N);

    for (valueType i = 0; i < N; ++i) {
        X[i] = distribution(engine);
        Y[i] = distribution(engine);
        D[i] = distribution(engine);
    }

    for (auto const &x: X)
        for (auto const &y: Y)
            for (auto const &d: D)
                print(x + d, y + d);

    std::cout << std::flush;

    return 0;
}