Loading

【题解】P4648 [IOI2007] pairs 动物对数

Problem

给定模板 \(B(1\le B \le 3)\),代表 \(B\) 维空间。其中有 \(n\) 个点,给出坐标与坐标上限 \(M\),求 \(n\) 个点中曼哈顿距离 \(\le D\) 的对数。

Solve

\(B = 1\)

考虑将问题化简成:求 \(\sum\limits_{i=1}^n\sum\limits_{j=1}^{i-1}[dis(i,j)\leq D]\)。其中 \(dis(i,j)\) 表示第 \(i,j\) 个点的距离。

然后考虑将一维坐标排序,枚举 \(i\)。发现每一次 \(i\) 做出贡献的位置 \(j<i\)\(dis(i,j)\leq D\),所以每次将 \(i\) 加入队列中,如果队首对应坐标与当前坐标距离大于 \(D\),就不停的弹队首,直至满足条件。然后将队列长度加入答案贡献(此时队列里的所有对应坐标都能与 \(i\) 一起贡献答案)。

时间复杂度 \(O(n)\)

点击查看代码
namespace Solve1 {
  int a[N], q[N], h = 1, t = 0, ans = 0;
  void work() {
    For(i,1,n) cin >> a[i];
    sort(a + 1, a + n + 1);
    For(i,1,n) {
      while(t - h + 1 > 0 && a[i] - a[q[h]] > d) h++;
      ans += (t - h + 1);
      q[++t] = i;
    }
    cout << ans << '\n';
  }
}

\(B=2\)

考虑降位。

第一维可以参考 \(B = 1\) 的情况排序后扫掉,第二维可以树状数组进行维护。

但是我们注意到,答案贡献的领域是一个斜 \(45^\circ\) 正方形(因为距离是按曼哈顿距离来统计的)。

所以我们只需要将曼哈顿距离转成且切比雪夫距离,这样答案贡献的领域就是一个正的,长度为 \(2D\) 的正方形了。

考虑如何转:

\[\begin{aligned} d(x,y)&=|x_1-x_2|+|y_1-y_2|\\ &=\max\{x_1-x_2+y_1-y_2, x_1-x_2-y_1+y_2,-x_1+x_2+y_1-y_2,-x_1+x_2-y_1+y_2\}\\ &=\max\{|(x_1+y_1)-(x_2+y_2)|, |(x_1-y_1)-(x_2-y_2)|\} \end{aligned} \]

于是将原坐标 \((x,y)\) 转成 \((x+y,x-y)\),在新坐标下维护操作即可。

时间复杂度 \(O(n\log M)\)

点击查看代码
namespace Solve2 {
  struct Node {
    int x, y;
  } a[N];
  int q[N], h = 1, t = 0, Ans = 0;
  
  Int T[N * 2] = {0};
  
  bool cmp(Node x, Node y) {
    return (x.x == y.x ? x.y < y.y : x.x < y.x);
  }

  int lb(int x) {
    return x & -x;
  }

  void add(int x, int k) {
    for (int i = x; i <= 2 * m; i += lb(i)) {
      T[i] += k;
    }
  }

  int ask(int x) {
    int ans = 0;
    for (int i = min(2 * m, x); i; i -= lb(i)) {
      ans += T[i];
    }
    return ans;
  }

  void work() {
    For(i,1,n) cin >> a[i].x >> a[i].y;
    For(i,1,n) {
      int x = (a[i].x - a[i].y), y = (a[i].x + a[i].y);
      a[i].x = x, a[i].y = y;
    }
    sort(a + 1, a + n + 1, cmp);
    For(i,1,n) {
      while(t - h + 1 > 0 && a[i].x - a[q[h]].x > d) {
        add(a[q[h]].y, -1);
        h++;
      }
      Ans += (ask(a[i].y + d) - ask(max(0ll, a[i].y - d - 1)));
      add(a[i].y, 1);
      q[++t] = i;
    }
    cout << Ans << '\n';
  }
}

\(B = 3\)

发现 \(M\le 75\),于是可以往高维树状数组想。

\(B=2\) 一样,先将曼哈顿距离转成且切比雪夫距离,对于三维。

考虑如何转:

\[\begin{aligned} d(x,y)&=|x_1-x_2|+|y_1-y_2|+|z_1-z_2|\\ &=\max\{\\ &(x_1-x_2+y_1-y_2+z_1-z_2),\\ &(x_1-x_2+y_1-y_2-z_1+z_2),\\ &(x_1-x_2-y_1+y_2+z_1-z_2),\\ &(x_1-x_2-y_1+y_2-z_1+z_2),\\ &(-x_1+x_2+y_1-y_2+z_1-z_2),\\ &(-x_1+x_2+y_1-y_2-z_1+z_2),\\ &(-x_1+x_2-y_1+y_2+z_1-z_2),\\ &(-x_1+x_2-y_1+y_2-z_1+z_2)\\ \}\\ &=\max\{\\ &|(x_1+y_1+z_1)-(x_2+y_2+z_2)|,\\ &|(x_1-y_1-z_1)-(x_2-y_2-z_2)|,\\ &|(x_1+y_1-z_1)-(x_2+y_2-z_2)|,\\ &|(x_1-y_1+z_1)-(x_2-y_2+z_2)|\\ \} \end{aligned} \]

是将原坐标 \((x,y,z)\) 转成 \((x-y-z,x+y+z,x+y-z,x-y+z)\),扫第一维,然后用三维树状数组维护前缀和即可。

时间复杂度 \(O(n\log^3 M)\)

点击查看代码
namespace Solve3 {
  const int L = 76;

  struct Node {
    int x, y, z, l;
  } a[N];
  int q[N], h = 1, t = 0, Ans = 0;
  
  Int T[226][305][305] = {0};
  
  bool cmp(Node x, Node y) {
    return (x.x == y.x ? x.y < y.y : x.x < y.x);
  }

  int lb(int x) {
    return x & -x;
  }

  void add(int x, int y, int z, int X) {
    for (int i = x; i <= 3 * m; i += lb(i)) {
      for (int j = y; j <= 3 * m + L; j += lb(j)) {
        for (int k = z; k <= 3 * m + L; k += lb(k)) {
          T[i][j][k] += X;
        }
      }
    }
  }

  int ask(int x, int y, int z) {
    int ans = 0;
    for (int i = min(3 * m, x); i; i -= lb(i)) {
      for (int j = min(3 * m + L, y); j; j -= lb(j)) {
        for (int k = min(3 * m + L, z); k; k -= lb(k)) {
          ans += T[i][j][k];
        }
      }
    }
    return ans;
  }

  void work() {
    For(i,1,n) cin >> a[i].x >> a[i].y >> a[i].z;
    For(i,1,n) {
      int x = (a[i].x - a[i].y - a[i].z), y = (a[i].x + a[i].y + a[i].z), 
          z = (a[i].x + a[i].y - a[i].z) + L, l = (a[i].x - a[i].y + a[i].z) + L;
      a[i].x = x, a[i].y = y, a[i].z = z, a[i].l = l;
    }
    sort(a + 1, a + n + 1, cmp);
    For(i,1,n) {
      while(t - h + 1 > 0 && a[i].x - a[q[h]].x > d) {
        add(a[q[h]].y, a[q[h]].z, a[q[h]].l, -1);
        h++;
      }
      int x1 = a[i].y + d, y1 = a[i].z + d, z1 = a[i].l + d;
      int x2 = max(0ll, a[i].y - d - 1), y2 = max(0ll, a[i].z - d - 1), z2 = max(0ll, a[i].l - d - 1);
      Ans += (ask(x1, y1, z1) - ask(x2, y1, z1) - ask(x1, y2, z1) - ask(x1, y1, z2)
      + ask(x2, y1, z2) + ask(x1, y2, z2) + ask(x2, y2, z1) - ask(x2, y2, z2));
      add(a[i].y, a[i].z, a[i].l, 1);
      q[++t] = i;
    }
    cout << Ans << '\n';
  }
}

Code

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define Int int
#define For(i,l,r) for(int i=l;i<=r;++i)
#define FOR(i,r,l) for(int i=r;i>=l;--i)

using namespace std;

const int N = 1e5 + 10;

int B, n, d, m;

namespace Solve1 {
  int a[N], q[N], h = 1, t = 0, ans = 0;
  void work() {
    For(i,1,n) cin >> a[i];
    sort(a + 1, a + n + 1);
    For(i,1,n) {
      while(t - h + 1 > 0 && a[i] - a[q[h]] > d) h++;
      ans += (t - h + 1);
      q[++t] = i;
    }
    cout << ans << '\n';
  }
}

namespace Solve2 {
  struct Node {
    int x, y;
  } a[N];
  int q[N], h = 1, t = 0, Ans = 0;
  
  Int T[N * 2] = {0};
  
  bool cmp(Node x, Node y) {
    return (x.x == y.x ? x.y < y.y : x.x < y.x);
  }

  int lb(int x) {
    return x & -x;
  }

  void add(int x, int k) {
    for (int i = x; i <= 2 * m; i += lb(i)) {
      T[i] += k;
    }
  }

  int ask(int x) {
    int ans = 0;
    for (int i = min(2 * m, x); i; i -= lb(i)) {
      ans += T[i];
    }
    return ans;
  }

  void work() {
    For(i,1,n) cin >> a[i].x >> a[i].y;
    For(i,1,n) {
      int x = (a[i].x - a[i].y), y = (a[i].x + a[i].y);
      a[i].x = x, a[i].y = y;
    }
    sort(a + 1, a + n + 1, cmp);
    For(i,1,n) {
      while(t - h + 1 > 0 && a[i].x - a[q[h]].x > d) {
        add(a[q[h]].y, -1);
        h++;
      }
      Ans += (ask(a[i].y + d) - ask(max(0ll, a[i].y - d - 1)));
      add(a[i].y, 1);
      q[++t] = i;
    }
    cout << Ans << '\n';
  }
}

namespace Solve3 {
  const int L = 76;

  struct Node {
    int x, y, z, l;
  } a[N];
  int q[N], h = 1, t = 0, Ans = 0;
  
  Int T[226][305][305] = {0};
  
  bool cmp(Node x, Node y) {
    return (x.x == y.x ? x.y < y.y : x.x < y.x);
  }

  int lb(int x) {
    return x & -x;
  }

  void add(int x, int y, int z, int X) {
    for (int i = x; i <= 3 * m; i += lb(i)) {
      for (int j = y; j <= 3 * m + L; j += lb(j)) {
        for (int k = z; k <= 3 * m + L; k += lb(k)) {
          T[i][j][k] += X;
        }
      }
    }
  }

  int ask(int x, int y, int z) {
    int ans = 0;
    for (int i = min(3 * m, x); i; i -= lb(i)) {
      for (int j = min(3 * m + L, y); j; j -= lb(j)) {
        for (int k = min(3 * m + L, z); k; k -= lb(k)) {
          ans += T[i][j][k];
        }
      }
    }
    return ans;
  }

  void work() {
    For(i,1,n) cin >> a[i].x >> a[i].y >> a[i].z;
    For(i,1,n) {
      int x = (a[i].x - a[i].y - a[i].z), y = (a[i].x + a[i].y + a[i].z), 
          z = (a[i].x + a[i].y - a[i].z) + L, l = (a[i].x - a[i].y + a[i].z) + L;
      a[i].x = x, a[i].y = y, a[i].z = z, a[i].l = l;
    }
    sort(a + 1, a + n + 1, cmp);
    For(i,1,n) {
      while(t - h + 1 > 0 && a[i].x - a[q[h]].x > d) {
        add(a[q[h]].y, a[q[h]].z, a[q[h]].l, -1);
        h++;
      }
      int x1 = a[i].y + d, y1 = a[i].z + d, z1 = a[i].l + d;
      int x2 = max(0ll, a[i].y - d - 1), y2 = max(0ll, a[i].z - d - 1), z2 = max(0ll, a[i].l - d - 1);
      Ans += (ask(x1, y1, z1) - ask(x2, y1, z1) - ask(x1, y2, z1) - ask(x1, y1, z2)
      + ask(x2, y1, z2) + ask(x1, y2, z2) + ask(x2, y2, z1) - ask(x2, y2, z2));
      add(a[i].y, a[i].z, a[i].l, 1);
      q[++t] = i;
    }
    cout << Ans << '\n';
  }
}

signed main() {
  ios::sync_with_stdio(0);
  cin.tie(0), cout.tie(0);
  cin >> B >> n >> d >> m;
  if(B == 1) {
    Solve1::work();
  } else if(B == 2) {
    Solve2::work();
  } else {
    Solve3::work();
  }
  return 0;
}
posted @ 2024-07-21 17:50  Daniel_yzy  阅读(23)  评论(0编辑  收藏  举报