CF624div3-F. Moving Points
题目
意思很简单,就是在一个x轴上,有N个有速度的点,他们可以向左向右的速度,问在之后的移动过程中,两两点点距离差之和最小是多少。
分析
因为这是一个匀速运动,我们不妨把两个匀速运动方程做差,做差之后的运动方程就是就是两个点运动过程中的距离之差方程。
我们可以发现,只有和
同号两个点才不会相撞,因为他们的距离差是越来越大,所以我们可以通过排序固定为
.所以只存在下面这种情况,当t = 0时,他们的距离差最小。
现在就遍历,每遍历一个就看在它之前有多少个(设为t)速度是小于等于它的,以及速度小于等于它的所有点离x = 0的距离和是多少(设为y),然后当前的最小距离和就是:x*t-y。现在就可以想到要用线段树来做来,先把点按坐标进行排序,然后一个一个插入到线段树中,并返回t和y(用pair存)
AC 代码
#include <iostream>
#include <algorithm>
#define X first
#define Y second
using namespace std;
const int maxn = 2e5+10;
typedef long long ll;
typedef pair<ll,ll> pii;
int N;
pii arr[maxn];
struct node{
int l,r;
pii pre;
}tr[4*maxn];
ll V[maxn],tail; //用于离散化
void pushup(int u){
tr[u].pre.first = tr[u*2].pre.first+tr[u*2+1].pre.first; //前缀和
tr[u].pre.second = tr[u*2].pre.second+tr[u*2+1].pre.second; //个数
}
void build(int u,int l,int r){
tr[u] = {l,r};
if(l == r) return ;
int mid = l+r>>1;
build(u*2,l,mid);
build(u*2+1,mid+1,r);
}
void modify(int u,int idx,int x){
if(tr[u].l == tr[u].r && tr[u].r == idx){
tr[u].pre.first += x;
tr[u].pre.second += 1;
}else{
int mid = (tr[u].l+tr[u].r)>>1;
if(idx<=mid) modify(u*2,idx,x);
else modify(u*2+1,idx,x);
pushup(u);
}
}
pii query(int u,int l,int r){
if(tr[u].l >=l && tr[u].r<=r){
return tr[u].pre;
}else{
int mid = tr[u].l+tr[u].r>>1;
pii p1,p2,p3;
if(l<=mid) p2 = query(u*2,l,r);
if(r>mid) p3 = query(u*2+1,l,r);
p1.first = p2.first+p3.first;
p1.second = p2.second+p3.second;
return p1;
}
}
int ind(ll x){
return lower_bound(V,V+tail,x)-V+1;//加1是因为我线段树下标从1开始的
}
int main(){
cin>>N;
build(1,1,N);
for(int i = 0;i<N;i++) scanf("%lld",&arr[i].X);
for(int i = 0;i<N;i++) {
scanf("%lld",&arr[i].Y);
V[tail++] = arr[i].Y;
}
sort(arr,arr+N);
sort(V,V+tail);
tail = unique(V,V+tail)-V;//排序后去重
ll res = 0;
for(int i = 0;i<N;i++){
pii p = query(1,1,ind(arr[i].Y));
res += arr[i].X*p.second - p.first;
modify(1,ind(arr[i].Y),arr[i].X);
}
cout<<res<<endl;
return 0;
} 
京公网安备 11010502036488号