題目大意
數軸上有個點(),給出每個點的座標 )以及速度,問在無窮時間範圍內,任意兩點間的最小距離之和是多少?(即求 的值)
分析過程
首先我們分析對於數軸上的兩個點(假設)來說:如果,那麼兩個點一定會在某一個時刻相遇;反之,兩點的初始位置之差便是兩點的最小距離。因此,對於任意一個點,我們只需要統計在它左側的速度比它小的點與它距離的絕對值之和即可。假設左側的速度比其小的位置爲,則我們所求即爲:(其中,爲比速度小的位置點數)
所以我們其實只需要知道左側比它小的位置點的個數以及他們的和就可以了,這是一個典型的具備前綴特徵的情境。不難想到,我們可以用樹狀數組來解決這個問題。我們使用兩個樹狀數組,其中一個用於存儲的前綴和(以樹狀數組下標作爲值域,用於很大這裏需要離散化一下),另一個存儲對應點的位置,以便求出前綴和。
綜上所述,本題做法如下:先對位置進行升序排序,然後離散化它們的,從左到右遍歷數組,每一次根據其速度對應的樹狀數組的前綴計算出結果並累加到中,然後將該點也加入到樹狀數組中。時間複雜度爲
AC代碼
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 200;
//bt[0]數組用於標記第i個位置之前比其速度小的座標個數
//bt[1]用於表示累計的座標前綴和
ll n,arr[maxn],bt[2][maxn];
struct Point{
ll x,v;
bool operator()(Point a, Point b){
return a.x < b.x;
}
}p[maxn];
int low_bit(int x){
return x & -x;
}
void add(int pos,int i,ll value){
while(pos <= n){
bt[i][pos] += value;
pos += low_bit(pos);
}
}
ll Query(int pos,int i){
ll sum = 0;
while(pos > 0){
sum += bt[i][pos];
pos -= low_bit(pos);
}
return sum;
}
void preDeal(){
int i;
sort(arr+1,arr+1+n);
int size = unique(arr+1,arr+1+n) - arr - 1;
for(i=1;i<=n;++i){
p[i].v = lower_bound(arr+1,arr+1+size,p[i].v) - arr;
}
}
int main(){
int i,j;
ll ans = 0;
ios::sync_with_stdio(false);
cin>>n;
for(i=1;i<=n;++i) cin>>p[i].x;
for(i=1;i<=n;++i){
cin>>p[i].v;
arr[i] = p[i].v;
}
preDeal();
sort(p+1,p+1+n,Point());
for(i=1;i<=n;++i){
ans += p[i].x * Query(p[i].v,0) - Query(p[i].v,1);
add(p[i].v,0,1);
add(p[i].v,1,p[i].x);
}
cout<<ans;
return 0;
}