分治36分求助!
查看原帖
分治36分求助!
550074
cloudemakers楼主2023/3/14 13:04

这题断断续续做了几次了。。。思路有没有问题? 用f1,f2,f3分别存储x,y,x*y的和, 需要调用的时候差分求区间和就行了;主要还是公式什么的有没有问题。。。

公式(类比归并排序): 对于已经把v从小到大排好的序列,按照x(在这里是tot数组)的大小来进行插入 每插入一次,分为左区间和右区间两种情况 分别按照题意数学推导即可(处理数据是为了保证符合题意的绝对值以及max)

#include<bits/stdc++.h>
#define maxn 600000
using namespace std;
int tot[maxn],n,x[maxn],v[maxn],k[maxn],flag[maxn];
unsigned long long f1[maxn],f2[maxn],f3[maxn];
unsigned long long ans;
vector<int> a[maxn];
void test1(){//调试用 
	for (int i=1;i<=n;i++) cout<<f1[i]<<" ";
	cout<<endl;
	for (int i=1;i<=n;i++) cout<<f2[i]<<" ";
	cout<<endl;
	for (int i=1;i<=n;i++) cout<<f3[i]<<" ";
	cout<<endl;
}
void solve(int l,int r){
	if (l==r) return;
	int mid=(l+r)>>1;
	solve(l,mid);solve(mid+1,r);
	for (int ii=l,i=l,j=mid+1;ii<=r;ii++){
		if (i==mid+1){
		//	cout<<"加入j一次;"<<(i-l)*v[j]*tot[j]-v[j]*(f1[i-1]-f1[l-1])<<endl;
			ans+=(i-l)*v[j]*tot[j]-v[j]*(f1[i-1]-f1[l-1]);
			k[ii]=tot[j++];
		}
		else if (j==r+1){
		//	cout<<"加入i一次;"<<tot[i]*(f2[j-1]-f2[mid])-(f3[j-1]-f3[mid])<<endl; 
			ans+=tot[i]*(f2[j-1]-f2[mid])-(f3[j-1]-f3[mid]);
			k[ii]=tot[i++];		
		}
		else if (tot[i]<=tot[j]){
		//	cout<<"加入i一次;"<<tot[i]*(f2[j-1]-f2[mid])-(f3[j-1]-f3[mid])<<endl; 
			ans+=tot[i]*(f2[j-1]-f2[mid])-(f3[j-1]-f3[mid]);
			k[ii]=tot[i++];
		}
		else{
		//	cout<<"加入j一次;"<<(i-l)*v[j]*tot[j]-v[j]*(f1[i-1]-f1[l-1])<<endl;
			ans+=(i-l)*v[j]*tot[j]-v[j]*(f1[i-1]-f1[l-1]);
			k[ii]=tot[j++];
		}
	}
	for (int i=l;i<=r;i++) tot[i]=k[i];
}
int main(){
	scanf("%d",&n);
	for (int i=1;i<=n;i++){
		scanf("%d%d",&v[i],&x[i]);
		a[v[i]].push_back(x[i]);
	}
	sort(v,v+n+1);
	int tem=1;
	for (int i=1;i<=n;i++){
		if (!flag[v[i]]){
			flag[v[i]]=1;
			for (int j=0;j<a[v[i]].size();j++)
				tot[tem++]=a[v[i]][j];	
		}
	}
	tem--;
	for (int i=1;i<=n;i++){
		f1[i]=f1[i-1]+tot[i];
		f2[i]=f2[i-1]+v[i];
		f3[i]=f3[i-1]+v[i]*tot[i];
	}//f1:x f2:y f3:x*y
//	test1();
	solve(1,n);
	printf("%d",ans);
}


2023/3/14 13:04
加载中...