公式

G - AtCoder Wallpaper 解説 by Cyanmond


壁紙の模様はこの \(4 \times 2\) の長方形内の模様が無限に続いています。よって、 \(4 \times 2\) の長方形内にある \(8\) 個の \(1 \times 1\) の正方形それぞれについて、それが与えられた長方形内にいくつ存在するかを求めることで、この問題を解くことができます。

長方形内に各位置の正方形がいくつ存在するかを求めるとき、境界での \(1\) ずれなどが複雑かもしれません。一例として、下の実装では次のようにしています。

  • \(x, y\) 方向それぞれについていくつ正方形が並んでいるかを独立に求め、最後に掛け合わせることにする。
  • \(x\) 方向にいくつ並んでいるかを求めるときは、十分大きな \(4\) の倍数の定数 \(P\) (例えば \(1.1 \times 10^9\) など) をとって、 \((x = -P と x = C の間にある個数) - (x = -P と x = A の間にある個数)\) の形で求める。
    • \(P\) は負の値の割り算が \(0\) 方向に丸められることの回避のために用いています。
  • \(y\) 方向についても同様の形で求める。

また、このような問題のデバッグでは自分で色々なケースを作ってみるのも有用です。今回の問題では、原因が境界の \(1\) ずれであれば、 \(0 \leq A, B, C, D \leq 8\) ほどの範囲でいろいろなケースを試してみるとバグが見つかるはずです。

実装例 (C++) :

#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

constexpr int mass[2][4] = {
    {2, 1, 0, 1},
    {1, 2, 1, 0}
};
// mass[i][j] : 長方形 [[j, j + 1], [i, i + 1]] の面積 x 2

constexpr i64 inf = 4000000000ll;

int main() {
    // 入力
    i64 a, b, c, d;
    cin >> a >> b >> c >> d;

    // 計算
    i64 ans = 0;
    for (int fy = 0; fy < 2; ++fy) {
        for (int fx = 0; fx < 4; ++fx) {
            const i64 x1 = (a - fx + 3 + inf) / 4, x2 = (c - fx + 3 + inf) / 4;
            const i64 count_x = x2 - x1; // x 方向の個数
            const i64 y1 = (b - fy + 1 + inf) / 2, y2 = (d - fy + 1 + inf) / 2;
            const i64 count_y = y2 - y1; // y 方向の個数
            ans += count_x * count_y * mass[fy][fx];
        }
    }

    // 出力
    cout << ans << endl;
}

投稿日時:
最終更新: