寒假集训做题分享(1)

题目来源:

[ABC321D] Set Menu


此题我做了好久一直没有发现错误在哪,原本以为可能是关于二分的问题,最后通过单步调试才发现问题就出现在写的公式上(气死了,气死了)(单步调试万岁,嘻嘻


接下来是本题的思路,首先是暴力解法,时间复杂度是\(O(N^2)\),这显然是不行的。那么此题就需要使用二分进行优化。

1. 首先我们可以这样想,在所有套餐可能中有一些可能是重复的,也就是重复的 \(p\) ,另一部分则是 \(a [ i ]\)\(b [ i ]\) 的和\(sum\)


2. 那么我们要考虑区分p和sum的界限是什么,我们要找到区分这两个区间的值,这个值就是我们需要通过二分去寻找的,也就是p-b [ i ],以下用m代替此值。


3. 当 \(m <= 0\) 时,那么很明显所有可能的组合都是要比p大的,\(min(s,p)=p\) ; 所以总的价值就是\(n*p\)\({sum = n * p}\);


4.当m > 0时,两个区间分别的sum分别是 sssssssss.......pppppp...... ,对于p的处理不必多说,通过二分找到边界后,右区间的和sumr就是右区间 \(a [ i ]\) 元素的个数 \(* p\) ,\({sumr=(n - u + 1) * p}\);


而左区间我们可以通过前缀和来处理,\({suml=q [ u - 1 ] + (u - 1) * b[ i ] }\);


最后将\(suml\)\(sumr\)相加便是答案

#include<bits/stdc++.h>
using namespace std;
#define int long long
  //记得开long long
const int N = 2e5 + 10;
int a[N], b[N], q[N];
int n, m, p, i;
int sum, ans;
signed main() {
	cin >> n >> m >> p;
	for (i = 1; i <= n; i++) {
		scanf("%lld", &a[i]);
	}
	sort(a + 1, a + 1 + n);//对数组a排序
	for (i = 1; i <= n; i++) {
		q[i] = q[i - 1] + a[i];
        //前缀和
	}
	for (i = 1; i <= m; i++) {
		scanf("%lld", &b[i]);
		int m = p - b[i];
		if (m <= 0) {
			ans += n * p;
		} else {
        //使用stl函数偷懒。
			int u = upper_bound(a + 1, a + 1 + n, m) - a;
       //激情调试	cout << u << endl; 
                       //u就是我们要找的边界
			ans += (n - u + 1) * p;//右区间
//			cout << ans << " ";
			ans += q[u - 1] + (u - 1) * b[i];
//左区间,在这里一开始我的公式写错了,调了好久,企图偷懒的原因......
//			cout << ans << " ";
//			cout << ans << endl;
		}
	}
	cout << ans << endl;
}
/*
2 2 7
3 5
6 1

14
10
24

5 5 6
1 2 4 6 8
2 5 4 3 1
13 + 12
6 + 24
11 + 18
9 + 18
10 + 12
*/
posted @ 2024-07-05 17:34  ZhangDT  阅读(0)  评论(0编辑  收藏  举报