F. Moving Points
题意:
给定n个位置和对应速度,问在任意时间,使得两点之间的距离最小,求所有点对的最小距离和。
思路:
题意给出的情况只有两种;
1.如果xi < xj,并且vi <= vj,i点肯定不能与j点相遇,即d(i, j) = xj - xi.
2.其他情况都能相遇,即d(i, j) = 0.
在处理最小距离和时,只需要处理第一种情况即可,因为第一种情况才会对答案有贡献。
将所有点按照位移x排为升序,枚举所有点,查找v是第j大。
用一个二维的树状数组,0号位存有多少个比vj小的,1号位存比vj小的坐标和。
所以对当前答案的贡献 = 当前的坐标 * 有多少个比它小的坐标 - 比它小的坐标和;
时间复杂度为O(nlogn)
AC代码
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 5;
ll sum[2][maxn], disv[maxn];
struct node{
int x, v;
}s[maxn];
int n;
bool cmp(const node& a, const node& b){
return a.x < b.x;
}
int lowbit(int x){
return x & (-x);
}
void update(int pos, int num){
while (pos <= n){
sum[0][pos]++;
sum[1][pos] += num;
pos += lowbit(pos);
}
}
ll getSum(int pos, int k){
ll res = 0;
while (pos > 0){
res += sum[k][pos];
pos -= lowbit(pos);
}
return res;
}
void solve(){
scanf("%d", &n);
for (int i = 0; i < n; ++i){
scanf("%d", &s[i].x);
}
for (int i = 0; i < n; ++i){
scanf("%d", &s[i].v);
disv[i] = s[i].v;
}
sort(s, s + n, cmp);
sort(disv, disv + n);
int len = unique(disv, disv + n) - disv;
ll ans = 0;
for (int i = 0; i < n; ++i){
int j = lower_bound(disv, disv + len, s[i].v) - disv;
//这里位置需要+1,因为树状数组是从小标1开始的。
ans += s[i].x * getSum(j + 1, 0) - getSum(j + 1, 1);
update(j + 1, s[i].x);
}
printf("%lld\n", ans);
}
int main(){
solve();
return 0;
}