--- tags: IOI --- # IOI2007 Day2-2 ペア (Pairs) 1種類の実装で満点を取れるようにしなさーい! ## 問題 https://www.ioi-jp.org/ioi/2007/problem/day2/pairs-prob-j.pdf https://oj.uz/problem/view/IOI07_pairs $M \times M \times \dots$ の $B$ 次元の超立方体のグリッド上に、$N$ 個の動物が置かれている。 マンハッタン距離が $D$ 以下のペアは何組あるか? $B ≤ 3$ $M ≤ 75\ (B = 3)$ $M ≤ 75000\ (B = 2)$ $N ≤ 10^5$ ## 考察 ひし形は 45° 回転!(素振り) 矩形和になったので kD-Tree が楽 $O(N^{2-\frac{1}{B}})$ →WA $B = 3$ のとき、求めるのは正八面体の和なので、実際には $2^{B-1}$ 次元になってる(!) →TLE… さすがに $O(N^\frac{5}{3})$ はきつかったので、セグ木を使う $B = 1$ のときはしゃくとり $B = 2$ のときはセグ木になるので、問題は $B = 3$ 4次元セグ木もきつそうなので、 平面走査と $M ≤ 75$ を利用して 3次元セグ木 $O(N(\log M)^3 + M^3)$ にする ## 実装 1点加算区間取得なので、実装は思ったより少ない。 各層を別々のstructにして、コピペをすると楽。 ちなみに model code は 3次元BITを全ての $B$ に対して使っている… https://oj.uz/submission/216085 ```cpp #include <bits/stdc++.h> using namespace std; using pii = pair<int, int>; using ll = long long; #define all(a) begin(a), end(a) #define each(i,a) for(auto&& i : a) #define overload3(_1, _2, _3, name, ...) name #define rep1(n) for(int i = 0; i < (n); i++) #define rep2(i, n) for(int i = 0; i < (n); i++) #define rep3(i, a, b) for(int i = (a); i < (b); i++) #define rep(...) overload3(__VA_ARGS__, rep3, rep2, rep1)(__VA_ARGS__) template<class T, class U> bool chmin(T& a, const U& b){ if(a > b){ a = b; return 1; } return 0; } template<class T, class U> bool chmax(T& a, const U& b){ if(a < b){ a = b; return 1; } return 0; } struct SegTree1{ static constexpr int size = 225; int data[size * 2]; void add(int x, int val){ do{ data[x] += val; }while(x /= 2); } int get(int x1, int x2){ int ans = 0; for(; x1 < x2; x1 /= 2, x2 /= 2){ if(x1 & 1) ans += data[x1++]; if(x2 & 1) ans += data[--x2]; } return ans; } }; struct SegTree2{ static constexpr int size = 225; SegTree1 data[size * 2]; void add(int x, int y, int val){ do{ data[x].add(y, val); }while(x /= 2); } int get(int x1, int x2, int y1, int y2){ int ans = 0; for(; x1 < x2; x1 /= 2, x2 /= 2){ if(x1 & 1) ans += data[x1++].get(y1, y2); if(x2 & 1) ans += data[--x2].get(y1, y2); } return ans; } }; struct SegTree3{ static constexpr int size = 225; SegTree2 data[size * 2]; void add(int x, int y, int z, int val){ x += size; y += size; z += size; do{ data[x].add(y, z, val); }while(x /= 2); } int get(int x1, int x2, int y1, int y2, int z1, int z2){ chmax(x1, 0); chmin(x2, size); chmax(y1, 0); chmin(y2, size); chmax(z1, 0); chmin(z2, size); x1 += size; x2 += size; y1 += size; y2 += size; z1 += size; int ans = 0; z2 += size; for(; x1 < x2; x1 /= 2, x2 /= 2){ if(x1 & 1) ans += data[x1++].get(y1, y2, z1, z2); if(x2 & 1) ans += data[--x2].get(y1, y2, z1, z2); } return ans; } }; struct SegTree{ static constexpr int size = 200000; int data[size * 2]; void add(int x, int val){ x += size; do{ data[x] += val; }while(x /= 2); } int get(int x1, int x2){ chmax(x1, 0); chmin(x2, size); x1 += size; x2 += size; int ans = 0; for(; x1 < x2; x1 /= 2, x2 /= 2){ if(x1 & 1) ans += data[x1++]; if(x2 & 1) ans += data[--x2]; } return ans; } }; int main(){ cin.tie(nullptr); ios::sync_with_stdio(false); int b, n, d, m; cin >> b >> n >> d >> m; ll ans = 0; if(b == 1){ vector<int> a(n); each(i, a) cin >> i; sort(all(a)); int min = 0; rep(n){ while(a[i] - a[min] > d) min++; ans += i - min; } } else if(b == 2){ vector<array<int, 2>> a(n); each(i, a){ cin >> i[0] >> i[1]; i[0]--; i[1]--; } each(i, a) i = {i[0] + i[1], i[0] - i[1] + 74999}; sort(all(a)); SegTree seg; int min = 0; rep(n){ while(a[i][0] - a[min][0] > d){ seg.add(a[min][1], -1); min++; } ans += seg.get(a[i][1] - d, a[i][1] + d + 1); seg.add(a[i][1], 1); } } else{ vector<array<int, 4>> a(n); each(i, a){ cin >> i[0] >> i[1] >> i[2]; i[0]--; i[1]--; i[2]--; } each(i, a) i = {i[0] + i[1] + i[2], -i[0] + i[1] + i[2] + 74, i[0] - i[1] + i[2] + 74, i[0] + i[1] - i[2] + 74}; sort(all(a)); static SegTree3 seg; int min = 0; rep(n){ while(a[i][0] - a[min][0] > d){ seg.add(a[min][1], a[min][2], a[min][3], -1); min++; } ans += seg.get(a[i][1] - d, a[i][1] + d + 1, a[i][2] - d, a[i][2] + d + 1, a[i][3] - d, a[i][3] + d + 1); seg.add(a[i][1], a[i][2], a[i][3], 1); } } cout << ans << endl; } ```