AtCoder Beginner Contest 351 E Solution
AT_abc351_e Jump Distance Sum
题目简述
一个平面直角坐标系上有 \(n\) 个点,定义两个点 \(a\) 和 \(b\) 之间的距离 \(\text{dist}(a,b)\) 如下。
定义一次合法移动,为将一个点 \((x,y)\) 移动至 \((x+1,y+1)\) 或 \((x+1,y-1)\) 或 \((x-1,y+1)\) 或 \((x-1,y-1)\) 。
若 \(a\) 可以通过有限次合法移动移动至点 \(b\) ,则 \(\text{dist}(a,b)\) 为 \(a\) 通过合法移动移动至 \(b\) 的最小次数。
若 \(a\) 无法通过有限次合法移动移动至点 \(b\) ,则 \(\text{dist}(a,b)=0\) 。
求
\[\sum_{i=1}^{n-1}\sum_{j=i+1}^n \text{dist}(p_i,p_j)
\]
省流
对顶点坐标进行重映射后分组通过前缀和分别求 \(x\) 坐标与 \(y\) 坐标的距离。
AtCoder 的思维性又回来了。
思路
由于点是斜向走的,直接求显然难度较大。
考虑对点的坐标重映射。
定义 \(p=x+y,q=x-y\) 。
原坐标系上一点 \(A(x,y)\) 的重映射为 \(A'(p,q)\) 。
则,题面中提到的移动 \((x+1,y+1)\) 或 \((x+1,y-1)\) 或 \((x-1,y+1)\) 或 \((x-1,y-1)\) ,将会分别被映射为 \((p+2,q)\) 或 \((p,q+2)\) 或 \((p,q-2)\) 或 \((p-2,q)\) 。
经过重映射后可以看出,可以互相到达的两个点 \(A\) 和 \(B\) ,他们的 \(p\) 和 \(q\) 的奇偶性一定相同。
于是就可以使用一般平面直角坐标系上曼哈顿距离的求解方法对距离进行求解。
最终的结果要除以 \(2\) 。
时间复杂度 \(O(n\log n)\) 。
代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
using ll = long long;
const int N = 2e5 + 10;
vector<ll> x[2][2], y[2][2];
int n, tx, ty;
ll res, tmp;
int main()
{
scanf("%d", &n);
for (int i = 1, sm, dv; i <= n; i++)
{
scanf("%d%d", &tx, &ty);
sm = tx + ty, dv = tx - ty;
x[sm % 2][abs(dv) % 2].push_back(sm);
y[sm % 2][abs(dv) % 2].push_back(dv);
}
for (int a = 0; a < 2; a++)
{
for (int b = 0; b < 2; b++)
{
sort(x[a][b].begin(), x[a][b].end());
sort(y[a][b].begin(), y[a][b].end());
tmp = 0;
for (int t = 1; t < x[a][b].size(); t++)
{
tmp += x[a][b][t] - x[a][b][0];
}
for (int t = 1; t < x[a][b].size(); t++)
{
res += tmp / 2;
tmp -= (x[a][b][t] - x[a][b][t - 1]) * (x[a][b].size() - t);
}
tmp = 0;
for (int t = 1; t < y[a][b].size(); t++)
{
tmp += y[a][b][t] - y[a][b][0];
}
for (int t = 1; t < y[a][b].size(); t++)
{
res += tmp / 2;
tmp -= (y[a][b][t] - y[a][b][t - 1]) * (y[a][b].size() - t);
}
}
}
printf("%lld\n", res);
}