传送门
这题可以采用分治的方法,类似于归并排序的思路。
其核心问题在于,我们怎么化简左右结合的步骤?
如果我们只是单纯的分别计算左右两两的音量,那就是假的分治,实则是暴力算法,复杂度也是O(n2)的,那就没有任何意义了,而且会tle。
那么我们怎么去处理呢?
我们先回忆一下归并排序为什么会比更直接的排序更快,关键在于合并的步骤,归并排序在左右合并时,巧妙地利用了左右两边分别有序的条件,从而使得合并步骤在O(n)的复杂度内完成。
所以我们也不妨对这个数据进行排序,那么我们应该按什么为标准进行排序呢?
我们可以在预处理时,按照v来排序,接下来分治的过程中,再按照x进行归并排序。
这样的话,每次合并时,我们就能保证右侧的v均大于左侧,且左侧和右侧的x是分别有序的。利用这两条性质就可以大大简化,具体的实现可以参考如下代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=20005;
int n;
struct Cow{
long long vv,xx;
}cow[maxn];
long long work(int l,int r){
if(l==r){
return 0;
}
int mid=(l+r)/2;
long long ans=0;
ans+=work(l,mid);
ans+=work(mid+1,r);
long long x1=0,x2=0;
for(int i=l;i<=mid;i++){
x1+=cow[i].xx;
}
int ind=l;
for(int i=mid+1;i<=r;i++){
while(ind<=mid&&cow[ind].xx<cow[i].xx){
x2+=cow[ind].xx;
x1-=cow[ind].xx;
ind++;
}
ans+=cow[i].vv*(x1-x2-(mid-ind+1)*cow[i].xx+(ind-l)*cow[i].xx);
}
Cow cowl[mid-l+1],cowr[r-mid];
for(int i=l;i<=r;i++){
if(i<=mid){
cowl[i-l]=cow[i];
}else{
cowr[i-mid-1]=cow[i];
}
}
int pl=0,pr=0;
for(int i=l;i<=r;i++){
if(pl>(mid-l)){
cow[i]=cowr[pr];
pr++;
}else if(pr>(r-mid-1)){
cow[i]=cowl[pl];
pl++;
}else if(cowl[pl].xx<cowr[pr].xx){
cow[i]=cowl[pl];
pl++;
}else{
cow[i]=cowr[pr];
pr++;
}
}
return ans;
}
int cmp(Cow a,Cow b){
return a.vv<b.vv;
}
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%lld%lld",&cow[i].vv,&cow[i].xx);
}
sort(cow,cow+n,cmp);
printf("%lld",work(0,n-1));
return 0;
}