【AtCoder】ABC 449 D - Make Target 2

実行時間制限: 2 sec / メモリ制限: 1024 MiB / Difficulty: 927 / NoviSteps: ??? / 配点: 425 点
問題概要
次元座標平面があり、座標が である格子点は が偶数のとき黒、奇数のとき白で塗られている。
かつ を満たす整数の組 のうち、座標 が黒く塗られているものの個数を求めよ。
制約
- 入力される値はすべて整数
考察
第1象限のみの場合について考える
対称性がある問題なので、まずは以下のような について考えよう。
- かつ の範囲の格子点で、黒く塗られているものの個数
ここで、 として一般性を失わない(縦に長い矩形領域)。
この矩形領域は、以下の2つの領域に分けられる。
- の正方形領域
- の長方形領域
正方形領域の計算
この領域では、 とすると、ある に対するL字型の上の格子点の数は 個となる。
黒く塗られるのは が偶数のときだから、 のときの点の数を足し合わせればよい。 これは初項 、公差 、最終項 、項数 の等差数列の和であるから、この領域の黒い格子点の個数を とすると、
となるが、これは の偶奇によって以下のように場合分けが発生する。
長方形領域の計算
この領域では常に が成り立つから、 座標が偶数の格子点はすべて黒である。
の範囲の偶数の個数は、
となる。
横方向には点が 個あるから、これを掛けることによって、この領域の黒い格子点の個数を は、
と計算できる。
以上により、 と求めることができた。
与えられた矩形領域の正領域への分割・移動
与えられる矩形領域は負の範囲を含むこともあるが、 を使うためには正領域に移さなければならない。
ここで、数直線上の区間 を、絶対値をとった 以上の区間に分割することを考える。
- : そのまま とする。
- : 区間全体を折り返して とする。
- : に分けて考え、前者を折り返して とする。
これを 軸 と 軸 それぞれについて処理すればよい。
包除原理を利用した正領域の矩形領域内の黒い格子点数の計算
最後に、 の正領域の矩形領域が与えられたときに、その中の黒い格子点の個数を求める。
これは、包除原理の要領で、 を用いて以下のように計算できる。
以上より、全体としては定数時間でこの問題を解くことができる。
詳細は実装例も参照のこと。
実装例
1.#include <bits/stdc++.h>2.using namespace std;3. 4.#define rep(i, start, end) for (auto i = (start); (i) < (end); (i)++)5. 6.using ll = long long;7.using Pair_ll = pair<ll, ll>;8. 9.// ======================================== //10. 11.ll f(ll x, ll y)12.{13. if (y < x)14. swap(x, y);15. 16. ll res = 0;17. if (x % 2 == 0)18. res += (x + 1) * (x + 2) / 2;19. else20. res += x * (x + 1) / 2;21. 22. res += (y / 2 - x / 2) * (x + 1);23. return res;24.}25. 26.int main()27.{28. ll L, R, D, U;29. cin >> L >> R >> D >> U;30. 31. auto calc = [](ll l, ll r, ll d, ll u) -> ll32. {33. return f(r, u) - f(l - 1, u) - f(r, d - 1) + f(l - 1, d - 1);34. };35. 36. auto convert_interval = [](ll l, ll r) -> vector<Pair_ll>37. {38. vector<Pair_ll> res;39. if (r >= 0)40. res.push_back({max(0LL, l), r});41. if (l < 0)42. res.push_back({max(1LL, -r), -l});43. 44. return res;45. };46. 47. vector<Pair_ll> lr = convert_interval(L, R);48. vector<Pair_ll> du = convert_interval(D, U);49. 50. ll ans = 0;51. for (auto &&[l, r] : lr)52. {53. for (auto &&[d, u] : du)54. {55. ans += calc(l, r, d, u);56. }57. }58. 59. cout << ans << endl;60. 61. return 0;62.}
実装時間: 45 分
コメント
場合分けを減らせるし定数時間で終わるしで、この解法は結構気に入っている。





