题目来源:
原题:
F. Moving Points
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output
There are nn points on a coordinate axis OXOX. The ii-th point is located at the integer point xixi and has a speed vivi. It is guaranteed that no two points occupy the same coordinate. All nn points move with the constant speed, the coordinate of the ii-th point at the moment tt (tt can be non-integer) is calculated as xi+t⋅vixi+t⋅vi.
Consider two points ii and jj. Let d(i,j)d(i,j) be the minimum possible distance between these two points over any possible moments of time (even non-integer). It means that if two points ii and jj coincide at some moment, the value d(i,j)d(i,j) will be 00.
Your task is to calculate the value ∑1≤i<j≤n∑1≤i<j≤n d(i,j)d(i,j) (the sum of minimum distances over all pairs of points).
Input
The first line of the input contains one integer nn (2≤n≤2⋅1052≤n≤2⋅105) — the number of points.
The second line of the input contains nn integers x1,x2,…,xnx1,x2,…,xn (1≤xi≤1081≤xi≤108), where xixi is the initial coordinate of the ii-th point. It is guaranteed that all xixi are distinct.
The third line of the input contains nn integers v1,v2,…,vnv1,v2,…,vn (−108≤vi≤108−108≤vi≤108), where vivi is the speed of the ii-th point.
Output
Print one integer — the value ∑1≤i<j≤n∑1≤i<j≤n d(i,j)d(i,j) (the sum of minimum distances over all pairs of points).
题意:有一个x轴,上面分布了n个位置为x1,x2.xn,速度为v1,v2...vn,的点,求点所有i,j(1<=i<j<=n)的最小的距离的总和。
题解思路:以右边为正方向,假设有两个点,一左一右,如果左边的点速度比右边的小,那么他们最小的距离在没运动的时候得到,如果左边的速度快,那么最终的最小距离就是左边的点追上右边的点,距离为0。题目可以理解成对于每个点,求在那个点左边并且速度比该点小的点的距离的和。这时候可以用到离散化和树状数组,将点的速度离散化,因为不需要知道他们速度的具体数值,只需要知道他们的大小关系。一个树状数组用于记录比该点速度小且位置在左边的坐标和,一个树状数组用于记录前一个树状数组里面有多少点,这样该点与其他点的距离和就可以变成比自己小的点数*该点的位置-左边的坐标和。
代码:
#include <iostream>
#include <algorithm>
#include <cmath>
#include<cstring>
#include<queue>
#include<map>
#include<cstdlib>
#include<cstdio>
#include<string>
#define ll long long
using namespace std;
int lowbit(int x)//用于在树状数组进行块的跳跃
{
return x&(-x);
}
const int maxn=2e5+10;
ll c[maxn][2];//用一个二维数组来存储两个树状数组
int n;
void add(ll x,ll v){//改变树状数组块的值
for(;x<=n;x+=lowbit(x))
{
c[x][0]++;//记录点数
c[x][1]+=v;//记录坐标和
}
}
ll getsum(ll x,ll k)//树状数组求前缀和
{
ll ans=0;
for(;x;x-=lowbit(x))
ans+=c[x][k];
return ans;
}
ll speed[maxn];//用于将速度离散化
struct node{
ll x,v;
}a[maxn];//用于记录点
bool cmp(node a,node b)//给a数组排序,以坐标x从小到大
{
return a.x<b.x;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);//关闭同步流
cin>>n;
for(int i=1;i<=n;++i)
cin>>a[i].x;//输入
for(int i=1;i<=n;++i)
{
cin>>a[i].v;//输入
speed[i]=a[i].v;//给speed 赋值
}
sort(a+1,a+n+1,cmp);
sort(speed+1,speed+n+1);//speed从小到大排序
int m=unique(speed+1,speed+n)-speed-1;//用unique去重,返回再减去第一个元素的位置,
//就能得到数组还剩多少个元素
ll ans=0;//记录答案
for(int i=1;i<=n;++i)
{
ll x=lower_bound(speed+1,speed+n+1,a[i].v)-speed;//用二分查找a[i].v在speed中的位置
//比如a[1].x是最小的,得到在speed中的位置后,a[1].x就可以为从小到大第x开始的前缀和做贡献,
//就算用点速度比a[1].v小,但他的坐标一定比a[i].x大,所以第一次进入循环时,ans只能+0,以1到n遍历
//的顺序保证了每个点利用树状数组进行计算是其中的组成坐标和的坐标一定比a[i].x小,且肯定速度比
//a[i].v小,a[i].x从小到大依次给树状数组以速度排序的坐标和做贡献
ans+=a[i].x*getsum(x,0)-getsum(x,1);//计算距离的和
add(x,a[i].x);//将a[i].x加入以从小到大顺序的第xd开始的前缀和
}
cout<<ans;
return 0;
}