题目: https://www.luogu.com.cn/problem/P2345
思路:
以前我从没认为归并排序这么有用。
看到这道题最开始的思路是显示从大往小地排序v,然后直接暴力求解,看起来不太可靠(其实数据较弱,我第一次就是这么过的😼😼😼)
对上述思路进行优化
对于题目中的max{vi,vj}×∣xi−xj∣ ,通过对v排序解决前方的max{vi,vj},解决∣xi−xj∣ 问题变成了优化方向。估计也很快想到思路,对于v1,x1,假设在数组中(对v排完序的数组),v1的后面有a个比x1小的,b个比x1大的,那么绝对值就可写成(a*x1-(比x1小的数的求和)+(比x1大的数的求和)-b*x1)。
归并排序是分块进行排序的,所以两块对x已经排序完毕的小块,前面一块的v比后面的v大,进行归并的时候就可以很容易的得到后一小块有多少数据的x是比前面一块中的某一个数据的x1大 或者小的数量。具体就见代码吧
代码
#include<iostream>
#include<algorithm>
using namespace std;
struct node{
long long v,x;
};
const int maxn=2e4+9;
node data[maxn],temp[maxn];
bool cmp(node a,node b)
{
return a.v>b.v;
}
long long ans=0;
void mergesort(int l,int r)
{
if(l>=r)return;
int mid=(l+r)/2;
mergesort(l,mid);
mergesort(mid+1,r);
int sum=0;
for(int i=mid+1;i<=r;i++)
sum+=data[i].x;
int i=l,j=mid+1,t=l,sum1=sum;
while(i<=mid&&j<=r)
{
if(data[i].x<data[j].x)
{
temp[t++]=data[i];
ans+=data[i].v*((j-mid-1)*data[i].x-(sum-sum1)
+sum1-(r-j+1)*data[i].x);
++i;
}
else
{
temp[t++]=data[j];
sum1-=data[j].x;
++j;
}
}
while(i<=mid)
{
temp[t++]=data[i];
ans+=data[i].v*((j-mid-1)*data[i].x-(sum-sum1)
+sum1-(r-j+1)*data[i].x);
++i;
}
while(j<=r)
{
temp[t++]=data[j];
sum1-=data[j].x;
++j;
}
for(int i=l;i<=r;i++)
data[i]=temp[i];
return;
}
int main()
{
//归并排序未免有点强大
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%lld%lld",&data[i].v,&data[i].x);
sort(data,data+n,cmp);
//我差不多有思路了
mergesort(0,n-1);
printf("%lld\n",ans);
}