【题解】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\) 的正方形了。
考虑如何转:
于是将原坐标 \((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\) 一样,先将曼哈顿距离转成且切比雪夫距离,对于三维。
考虑如何转:
是将原坐标 \((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;
}