题目大意
数轴上有 n n n个点( 2 ≤ n ≤ 2 ⋅ 1 0 5 2\le n\le2·10^5 2≤n≤2⋅105),给出每个点的坐标 x i ( 1 ≤ x i ≤ 1 0 8 x_i (1\le x_i\le10^8 xi(1≤xi≤108)以及速度 v i ( − 1 0 8 ≤ v i ≤ 1 0 8 ) v_i(-10^8\le v_i\le 10^8) vi(−108≤vi≤108),问在无穷时间范围内,任意两点间的最小距离之和是多少?(即求 ∑ 1 ≤ i < j ≤ n d ( i , j ) \sum\limits_{1\le i<j\le n}d(i,j) 1≤i<j≤n∑d(i,j) 的值)
分析过程
首先我们分析对于数轴上的两个点
x
i
和
x
j
x_i和x_j
xi和xj(假设
x
i
<
x
j
x_i<x_j
xi<xj)来说:如果
v
i
>
v
j
v_i>v_j
vi>vj,那么两个点一定会在某一个时刻相遇;反之,两点的初始位置之差便是两点的最小距离。因此,对于任意一个点
x
i
x_i
xi,我们只需要统计在它左侧的速度比它小的点与它距离的绝对值之和即可。假设
x
i
x_i
xi左侧的速度比其小的位置为
x
0
,
x
1
,
x
2
,
.
.
.
,
x
j
x_0,x_1,x_2,...,x_j
x0,x1,x2,...,xj,则我们所求即为:
(
x
i
−
x
0
)
+
(
x
i
−
x
1
)
+
(
x
i
−
x
2
)
+
.
.
.
+
(
x
i
−
x
j
)
=
m
⋅
x
i
+
∑
p
=
0
j
x
p
(x_i-x_0)+(x_i-x_1)+(x_i-x _2)+...+(x_i-x_j)=m·x_i+\sum_{p=0}^jx_p
(xi−x0)+(xi−x1)+(xi−x2)+...+(xi−xj)=m⋅xi+p=0∑jxp(其中,
m
m
m为比
x
i
x_i
xi速度小的位置点数)
所以我们其实只需要知道
x
i
x_i
xi左侧比它小的位置点的个数以及他们的和就可以了,这是一个典型的具备前缀特征的情境。不难想到,我们可以用树状数组来解决这个问题。我们使用两个树状数组,其中一个用于存储
v
i
v_i
vi的前缀和(以树状数组下标作为值域,用于
v
i
v_i
vi很大这里需要离散化一下),另一个存储对应点的位置,以便求出前缀和。
综上所述,本题做法如下:先对位置进行升序排序,然后离散化它们的
v
i
v_i
vi,从左到右遍历
x
x
x数组,每一次根据其速度对应的树状数组的前缀计算出结果并累加到
a
n
s
ans
ans中,然后将该点也加入到树状数组中。时间复杂度为
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)
AC代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 200;
//bt[0]数组用于标记第i个位置之前比其速度小的坐标个数
//bt[1]用于表示累计的坐标前缀和
ll n,arr[maxn],bt[2][maxn];
struct Point{
ll x,v;
bool operator()(Point a, Point b){
return a.x < b.x;
}
}p[maxn];
int low_bit(int x){
return x & -x;
}
void add(int pos,int i,ll value){
while(pos <= n){
bt[i][pos] += value;
pos += low_bit(pos);
}
}
ll Query(int pos,int i){
ll sum = 0;
while(pos > 0){
sum += bt[i][pos];
pos -= low_bit(pos);
}
return sum;
}
void preDeal(){
int i;
sort(arr+1,arr+1+n);
int size = unique(arr+1,arr+1+n) - arr - 1;
for(i=1;i<=n;++i){
p[i].v = lower_bound(arr+1,arr+1+size,p[i].v) - arr;
}
}
int main(){
int i,j;
ll ans = 0;
ios::sync_with_stdio(false);
cin>>n;
for(i=1;i<=n;++i) cin>>p[i].x;
for(i=1;i<=n;++i){
cin>>p[i].v;
arr[i] = p[i].v;
}
preDeal();
sort(p+1,p+1+n,Point());
for(i=1;i<=n;++i){
ans += p[i].x * Query(p[i].v,0) - Query(p[i].v,1);
add(p[i].v,0,1);
add(p[i].v,1,p[i].x);
}
cout<<ans;
return 0;
}