P2345 [USACO04OPEN] MooFest G题解

题目: 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); 
} 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值