MooFest G(USACO04OPEN)

传送门

这题可以采用分治的方法,类似于归并排序的思路。
其核心问题在于,我们怎么化简左右结合的步骤?
如果我们只是单纯的分别计算左右两两的音量,那就是假的分治,实则是暴力算法,复杂度也是O(n2)的,那就没有任何意义了,而且会tle。
那么我们怎么去处理呢?
我们先回忆一下归并排序为什么会比更直接的排序更快,关键在于合并的步骤,归并排序在左右合并时,巧妙地利用了左右两边分别有序的条件,从而使得合并步骤在O(n)的复杂度内完成。
所以我们也不妨对这个数据进行排序,那么我们应该按什么为标准进行排序呢?
我们可以在预处理时,按照v来排序,接下来分治的过程中,再按照x进行归并排序。
这样的话,每次合并时,我们就能保证右侧的v均大于左侧,且左侧和右侧的x是分别有序的。利用这两条性质就可以大大简化,具体的实现可以参考如下代码:

#include
#include
#include
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;
} 

你可能感兴趣的:(洛谷题解,分治,分治算法,归并排序,USAC,洛谷,排序)