CF624div3-F. Moving Points 【线段树】【离散化】
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; }