题目描述
难度分:1400
输入T(≤2×105)表示T组数据。所有数据的n之和≤2×105。
每组数据输入n(1≤n≤2×105)和两个长为n的数组a,b,元素范围在[1,109]。
- 把下标1,2,…,n分成两组,记作P和Q。
- 对于P中的下标i,计算a[i]的最大值m。
- 对于Q中的下标j,计算b[j]的和s。
输出max(m,s)的最小值。
例如a=[3,7,4,5],b=[2,1,2,4],如果P=[1,4],Q=[2,3],那么m=5,s=3,所以max(m,s)=5。
输入样例
4
4
3 7 4 5
2 1 2 4
4
1 2 3 4
3 3 3 3
2
1 2
10 10
2
10 10
1 2
输出样例
5
3
2
3
算法
二分答案
这个题一看要求的东西是最大值的最小值,想到的就是二分答案,然后写了一版二分,测了样例没问题就提交AC
了。过了之后可以分析一下单调性,对于一个给定的limit,我们希望所有被选出来的a[i]的最大值都不超过limit,可以直接遍历i∈[1,n],如果a[i]>limit就把b[i]累加到s上,只要遍历完成后s≤limit,这个limit就是可以达成的。此时我们发现,如果增大limit,肯定也是可以达成的,因为这时候满足a[i]≤limit的a[i]会更多,说明累加到s上的b[i]会更少,所以具有单调性,可以二分答案来做。
复杂度分析
时间复杂度
二分时的check函数时间复杂度为O(n),只遍历了一遍i∈[1,n]。外面的二分时间复杂度为O(log2A),其中A是元素的上界,本题中A≤109。算法整体的时间复杂度为O(nlog2A)。
空间复杂度
除了输入的数组a和b,仅使用了有限几个变量,因此额外空间复杂度为O(1)。
C++ 代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 200010;
int t, n, a[N], b[N];
bool check(int limit) {
long long s = 0;
for(int i = 1; i <= n; i++) {
if(a[i] > limit) {
s += b[i];
if(s > limit) return false;
}
}
return s <= limit;
}
int solve() {
int l = 1, r = 1e9;
while(l < r) {
int mid = l + r >> 1;
if(check(mid)) {
r = mid;
}else {
l = mid + 1;
}
}
return r;
}
int main() {
scanf("%d", &t);
while(t--) {
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
for(int i = 1; i <= n; i++) {
scanf("%d", &b[i]);
}
printf("%d\n", solve());
}
return 0;
}