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

Problem#

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

Solve#

B=1#

考虑将问题化简成:求 i=1nj=1i1[dis(i,j)D]。其中 dis(i,j) 表示第 i,j 个点的距离。

然后考虑将一维坐标排序,枚举 i。发现每一次 i 做出贡献的位置 j<idis(i,j)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 正方形(因为距离是按曼哈顿距离来统计的)。

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

考虑如何转:

d(x,y)=|x1x2|+|y1y2|=max{x1x2+y1y2,x1x2y1+y2,x1+x2+y1y2,x1+x2y1+y2}=max{|(x1+y1)(x2+y2)|,|(x1y1)(x2y2)|}

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

时间复杂度 O(nlogM)

点击查看代码
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#

发现 M75,于是可以往高维树状数组想。

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

考虑如何转:

d(x,y)=|x1x2|+|y1y2|+|z1z2|=max{(x1x2+y1y2+z1z2),(x1x2+y1y2z1+z2),(x1x2y1+y2+z1z2),(x1x2y1+y2z1+z2),(x1+x2+y1y2+z1z2),(x1+x2+y1y2z1+z2),(x1+x2y1+y2+z1z2),(x1+x2y1+y2z1+z2)}=max{|(x1+y1+z1)(x2+y2+z2)|,|(x1y1z1)(x2y2z2)|,|(x1+y1z1)(x2+y2z2)|,|(x1y1+z1)(x2y2+z2)|}

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

时间复杂度 O(nlog3M)

点击查看代码
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;
}

作者:Daniel-yao

出处:https://www.cnblogs.com/Daniel-yao/p/18313238

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   Daniel_yzy  阅读(26)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示