【CodeForces 1311F】Moving Points
链接:
题目大意:
一条数轴上有 \(n\) 个动点,每个动点的初始坐标是 \(x_i\),它们每秒会向右移 \(v_i\)。求出所有点对最短距离之和。
正文:
假设我们当前正在处理点对 \((i,j)\)。如果 \(v_i<v_j\):
那么它们的距离会越来越远,所以初始距离则是最短距离。
如果 \(v_i=v_j\):
那么它们的距离不会改变,初始距离还是最短距离。
如果 \(v_i>v_j\):
因为时间不一定是整数,所以它们必然会走到一个点,即距离为 \(0\)。
那么现在可以把题目转换成:\(x_i<x_j\) 且 \(v_i\leq v_j\) 的点对距离和。显然是二维偏序。维护两个树状数组 \(A,B\),分别维护的是已经维护了点的数量、已维护点到原点距离之和。那么答案是 \(\sum A\cdot x_i-B\)。
代码:
const int N = 2e5 + 10;
ll t[2][N], b[N];
int n, m;
ll ans;
void modify(int x, ll val, ll t[]){for (; x <= N - 10; x += x & -x) t[x] += val;}
ll query (int x, ll t[])
{
ll ans = 0;
for (; x; x -= x & -x) ans += t[x];
return ans;
}
struct node
{
int x, v;
}a[N];
bool cmp (node a, node b) {return a.x < b.x || (a.x == b.x && a.v < b.v);}
int main ()
{
scanf ("%d", &n);
for (int i = 1; i <= n; i++)
scanf ("%d", &a[i].x);
for (int i = 1; i <= n; i++)
scanf ("%d", &a[i].v), b[i] = a[i].v;
sort (a + 1, a + 1 + n, cmp);
sort (b + 1, b + 1 + n);
int k = unique(b + 1, b + 1 + n) - b;
for (int i = 1; i <= n; i++)
a[i].v = lower_bound(b + 1, b + 1 + k, a[i].v) - b;
for (int i = 1; i <= n; i++)
{
modify(a[i].v, a[i].x, t[0]);
modify(a[i].v, 1, t[1]);
ans += query(a[i].v, t[1]) * a[i].x - query(a[i].v, t[0]);
}
printf ("%lld\n", ans);
return 0;
}