Codeforces Round #624 (Div. 3) F. Moving Points(树状数组)_偏序

题意:给出直线上若干个不同动点的初始位置和速度,求每两个点之间能达到的最短距离之和。

思路:首先我们发现,若x1<x2,并且v1<=v2的话,则两个点之间的距离一定是非递减的,也就是说,这两个之间的最短距离就是他们初始时刻的距离,因此这道题本质上是找到x1<x2并且v1<v2的点对。我们发现其实是一道二维偏序的题。

对于该题,我们考虑将点按照初始横坐标排序,通过树状数组,我们能快速找到速度小于当前点的数量,为了求出距离,我们需要额外开一个数组存储信息。当然,由于速度范围较大,我们需要将速度离散化。

#include<set>
#include<queue>
#include<vector>
#include<string>
#include<math.h>
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 200005
#define ll long long
int n;
ll c1[maxn],c2[maxn],arr[maxn],p[maxn];
pair<int,int> points[maxn];
void update(int x,int val,ll sum[]){
	while(x<=n){
		sum[x]+=val;
		x+=x&-x;
	}
}
ll query(int x,ll sum[]){
	ll res=0;
	while(x){
		res+=sum[x];
		x-=x&-x;
	}
	return res;
}
int main(void){
	ll sum=0;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&points[i].first);
	for(int i=1;i<=n;i++){
		scanf("%d",&points[i].second);
		arr[i]=points[i].second;
	}
	sort(points+1,points+n+1);
	sort(arr+1,arr+n+1);
	unique(arr+1,arr+n+1);
	for(int i=1;i<=n;i++)
		p[i]=lower_bound(arr+1,arr+n+1,points[i].second)-arr;
	for(int i=1;i<=n;i++){
		update(p[i],points[i].first,c1);
		update(p[i],1,c2);
		sum+=query(p[i],c2)*points[i].first-query(p[i],c1);
	}
	printf("%lld\n",sum);
	return 0;
}