頭整理するのが大変だった
問題概要
個の格子点 (、、) がある。
これらの格子点を以下のルールに則って 色 () で塗る。
- 点 の、 とのマンハッタン距離が であるとき
- 点 を、色 % で塗る
各 について、その色で塗られた格子点が何個あるかを出力せよ。
制約
考えたこと
点 を頂点にもつ直方体の足し引きで実現できる。よって以下の場合に帰着して考えられる。
で表される 個の格子点について、各格子点を色 % で塗る。各色で塗られた格子点の個数をそれぞれ求めよ。
さらに、 をあらかじめ で割ったあまりにする前処理を行う (それ以外の部分は各色が同色ずつになる)。
このとき、各 マスについて、色 % を始点として連続 色分に一様に 1 を足していく操作を行えばよい。これはいもす法で実現できる。
よって全体の計算量は でできる。
コード
#include <bits/stdc++.h> using namespace std; void add(vector<long long> &res, long long a, long long b, long long c, int N, long long add) { long long offset = (a * b * c - (a%N) * (b%N) * (c%N)) / N; a %= N, b %= N, c %= N; vector<long long> diff(N+1, 0); for (int i = 0; i < a; ++i) { for (int j = 0; j < b; ++j) { int start = (i + j) % N, goal = (start + c) % N; if (start <= goal) diff[start]++, diff[goal]--; else diff[start]++, diff[N]--, diff[0]++, diff[goal]--; } } for (int i = 0; i < N; ++i) diff[i+1] += diff[i]; for (int i = 0; i < N; ++i) res[i] += (diff[i] + offset) * add; } int main() { vector<long long> v(3), p(3); for (int i = 0; i < 3; ++i) cin >> v[i]; for (int i = 0; i < 3; ++i) cin >> p[i]; int N; cin >> N; vector<long long> res(N, 0), a(3); for (int bit = 0; bit < (1<<3); ++bit) { for (int i = 0; i < 3; ++i) { if (bit & (1<<i)) a[i] = p[i]+1; else a[i] = v[i]-p[i]; } add(res, a[0], a[1], a[2], N, 1); } for (int i = 0; i < 3; ++i) { a[i] = 1; for (int bit = 0; bit < (1<<2); ++bit) { for (int j = 0; j < 2; ++j) { if (bit & (1<<j)) a[(i+j+1)%3] = p[(i+j+1)%3]+1; else a[(i+j+1)%3] = v[(i+j+1)%3] - p[(i+j+1)%3]; } add(res, a[0], a[1], a[2], N, -1); } } for (int i = 0; i < 3; ++i) { add(res, 1, 1, p[i]+1, N, 1); add(res, 1, 1, v[i]-p[i], N, 1); } add(res, 1, 1, 1, N, -1); for (int i = 0; i < N; ++i) { if (i) cout << " "; cout << res[i]; } cout << endl; }