CF457B Distributed Join

原题链接

题意简述

给定数组 \(a\)\(b\),大小为 \(n\)\(m\),每次操作可以把 \(a\) 数组或 \(b\) 数组里的任意位置的数复制到任意其他位置,其代价为该位置的值。最后需要满足 \(a\) 数组的任意位置与 \(b\) 数组的任意位置都被复制到了同一个地方。求最小代价。

题目分析

考虑 \(a\) 数组的位置 \(i\)\(b\) 数组的位置 \(j\),要将这两个位置复制到同一个地方,有两种情况:

  1. 直接把 \(a_i\) 复制到 \(b_j\)(假设 \(a_i \leq b_j\),反之同理)

  2. \(a_i\) 复制到另一个地方 \(a_k\),再把 \(b_j\) 复制到 \(a_k\)(也可以复制到 \(b_k\),同理)

第二种情况看似代价更大一点,但实际上同时满足了 \(b_j\)\(a_i\)\(a_k\) 复制到同一地方的条件。

于是考虑贪心。

\(sum_b\)\(b\) 数组所有元素的和,\(max_a\)\(a\) 数组的最大值,\(pos_a\)\(a\) 数组中最大值的位置。

假设将 \(b\) 数组的所有位置复制到 \(a\) 的所有位置上,因为 \(b\) 中每个数都要复制到 \(a\)\(n\) 次,所以总的操作代价是 \(n \times sum_b\)。当然这只用到了情况1,还没有用到情况2。

在上面的情况下\(b\) 数组的所有位置都已经复制到了 \(a_k\) 上,那如果我们把 \(a_i\) 复制到 \(a_k\) 呢?那是不是 \(b\) 数组就不用了复制到 \(a_i\) 了,因为它们都在 \(a_k\) 上。这样的代价是 \(a_i\),但我们的总代价又减少了复制到 \(a_i\) 上的代价 \(sum_b\)。于是当 \(a_i \leq sum_b\) 时,考虑用情况2。

怎么取 \(a_k\) 呢?可以将 \(k\)\(pos_a\),即 \(a_k\)\(max_a\)。因为这样总代价肯定会更小一点。

Q : 有没有可能可以先把 \(a_i\) 复制到 \(b_j\) 上,再把 \(b_j\) 复制到 \(a_k\) 上呢(即互相复制到对方数组上)?这样上面的做法就考虑不到这种情况了。

A : 可能,但是这等价于第2种情况,代价也一样。

Q : 为什么不能将 \(a\) 数组复制到 \(b\) 数组上呢?

A : 同理,两种情况取 \(\min\) 即可。

于是就做完了。时间复杂度 \(O(n)\),绰绰有余。

代码

#include<bits/stdc++.h>
using namespace std;
namespace my_std{
	#define ll long long
	#define pc putchar
	#define fr(i,x,y) for(register ll i=(x);i<=(y);i++)
	#define il inline
	il ll read(){
		ll sum=0,f=1;
		char ch=0;
		while(!isdigit(ch)){
			if(ch=='-') f=-1;
			ch=getchar();
		}
		while(isdigit(ch)){
			sum=sum*10+(ch^48);
			ch=getchar();
		}
		return sum*f;
	}
	il void write(ll x){
		if(x<0){
			x=-x;
			pc('-');
		}
		if(x>9) write(x/10);
		pc(x%10+'0');
	}
}
using namespace my_std;
ll n,m,a[100010],b[100010],maxa=0,posa,maxb=0,posb,suma=0,sumb=0,ans1=0,ans2=0;
//maxa:最大值 posa:最大值的位置 suma:a 数组所有数的和 ans1:把 b 复制到 a 的最小代价 
int main(){
	n=read();
	m=read();
	fr(i,1,n){
		a[i]=read();
		suma+=a[i];
		if(a[i]>maxa){
			maxa=a[i];
			posa=i;
		}
	}
	fr(i,1,m){
		b[i]=read();
		sumb+=b[i];
		if(b[i]>maxb){
			maxb=b[i];
			posb=i;
		}
	}
	fr(i,1,n){//分类讨论
		if(i==posa) ans1+=sumb;//如果 i 已经是最大值的位置了,就不用复制到自己 
		else if(a[i]<=sumb) ans1+=a[i];
		else ans1+=sumb;
	}
	fr(i,1,m){//同理 
		if(i==posb) ans2+=suma;
		else if(b[i]<=suma) ans2+=b[i];
		else ans2+=suma;
	}
	write(min(ans1,ans2));//取最小值 
}
posted @ 2021-09-07 19:57  AFewSuns  阅读(52)  评论(0编辑  收藏  举报