专栏文章

题解:CF2132E Arithmetics Competition

CF2132E题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mio4qnbc
此快照首次捕获于
2025/12/02 13:20
3 个月前
此快照最后确认于
2025/12/02 13:20
3 个月前
查看原文
[Problem Discription]\color{blue}{\texttt{[Problem Discription]}}
不提供题目翻译,仅提供简要题面。
给定两个数组 a1n,b1ma_{1 \dots n},b_{1 \dots m}qq 次询问。每次询问给定三个参数 x,y,zx,y,z,你需要求出在 {ai}\{ a_{i} \} 中选出不超过 xx 个数且在 {bi}\{ b_{i} \} 中选出不超过 yy 个数的前提下,两个数组一共选出恰好 zz 个数的最大的和。
1n,m,q2×1051 \leq n,m,q \leq 2 \times 10^{5}
[Analysis]\color{blue}{\texttt{[Analysis]}}
显然,肯定选最大的那些数答案最优。
首先考虑在没有 x,yx,y 的限制的情况下怎么求解答案。
归并排序类似的思路,我们把 {ai},{bi}\{ a_{i} \},\{ b_{i} \} 从大到小排序之后,逐位进行比较,谁打就优先选择谁。这样做是 O(n+m)O(n+m) 的。
可是现在有个数的限制,怎么办?
有一点还是可以确定的,就是如果最终 {ai}\{ a_{i} \} 中选择了 z1z_{1} 个数,{bi}\{ b_{i} \} 中选择了 z2z_{2} 个数,那么这 z1z_{1} 个数一定是 {ai}\{ a_{i} \} 中最大的 z1z_{1} 个数。
上面的简单问题的思路可以给我们另外一点启发:我们考虑什么时候会在 {bi}\{ b_{i} \} 中选数而不是在 {ai}\{ a_{i} \} 选数?其实只有两种情况:
  1. {ai}\{ a_{i} \} 中选出来的数的个数已经达到了上界要求。
  2. {bi}\{ b_{i} \} 中再选的这个数比 {ai}\{ a_{i} \} 中的某个数大,可以用它代替那个数。
所以现在就有这么一种思路:假设我们最开始全部选择 {ai}\{ a_{i} \} 中数(先别管超不超上界),现在考虑要把某些选择的机会留给 {bi}\{ b_{i} \}。我们发现,如果
bsazs+1b_{s} \geq a_{z-s+1}
那么在 {bi}\{ b_{i} \} 中选择 ss 个数是不劣于全部选择权都给 {ai}\{ a_{i} \} 的(在这个式子中 a,ba,b 都已经从大到小排序)。
相反的,如果
bs<azs+1b_{s}< a_{z-s+1}
那么一定不会选 bsb_{s},因此选 bsb_{s} 带来的收益不如选 azs+1a_{z-s+1}
因此满足 bs>azs+1b_{s}>a_{z-s+1} 的最大的 ss 就是最终答案需要在 bb 中选的数的个数。
且随着 ss 的增大,左侧在减小,右侧在增大,因此可以二分求出临界的 ss。单次询问是对数的时间复杂度。
总时间复杂度 O(nlogn+mlogm+qlog(n+m))O(n \log n+m \log m+q \log (n+m))
Code\color{blue}{\text{Code}}
CPP
const int N=2e5+100;
typedef long long ll;
 
int n,m,q,a[N<<1],b[N<<1];
ll prea[N<<1],preb[N<<1];
 
void Regulate(int *a,int n){
	sort(a+1,a+n+1);
	reverse(a+1,a+n+1);
}
 
int OZDY(){
	n=read();m=read();q=read();
	for(int i=1;i<=n;i++)
		a[i]=read();
	for(int i=1;i<=m;i++)
		b[i]=read();
	
	Regulate(a,n);
	Regulate(b,m);
	
	for(int i=n+1;i<=n+m;i++) a[i]=0;
	for(int i=m+1;i<=n+m;i++) b[i]=0;//记得清空
	
	for(int i=1;i<=n+m;i++)
		prea[i]=prea[i-1]+a[i];
	for(int i=1;i<=n+m;i++)
		preb[i]=preb[i-1]+b[i];
	b[0]=0x3f3f3f3f;//记得把 b[0] 设置成一个很大的数值,原因下面解释 
	
	for(int T=1;T<=q;T++){
		int x=read(),y=read(),z=read();
		x=min(x,min(n,z));y=min(y,min(m,z));
		
		if (z==0){
			printf("0\n");
			continue;
		}
		
		int l=z-x,r=y,res=z-x;
		//假设全选 a,二分让出几个给 b 
		while (l<=r){
			int mid=(l+r)>>1;//如果 l=0,r=1,那么 mid=0,此时如果 b[0] 没有被初始化成一个极大值的话,r=1 是不会被二分考虑到的
			
			if (b[mid]>=a[z-mid+1]){
				res=mid;
				l=mid+1;
			}
			else r=mid-1;
		}
		
		if (res>m) printf("%lld\n",preb[m]+prea[z-m]);
		else if (z-res>n) printf("%lld\n",prea[n]+preb[z-n]);
		else printf("%lld\n",preb[res]+prea[z-res]);
	}
	
	return 0;
}
 
int main(){
	int T=read();
	while (T--) OZDY();
	
	return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...