经过大半个月的蓝桥杯等待,终于拿到了省级一等奖!,接下来就是预备国赛了,继续刷题保持手感!
我们来看一下今天这个题目:一道经典的二分答案的题目:
看这个题目的时候我都看了好久(可能数学一般吧哈哈哈)
我们可以从中看到一个信息:
也就是W越大 我们的Y值越小 可以看出它符合单调所以我们考虑用二分
二分的模板:
取靠左:
while (l < r) {
int mid = l + r >> 1;
if (!check(mid)) l = mid + 1;
else r = mid;
}
取靠右:
while (l < r) {
int mid = l + r +1>> 1;
if (!check(mid)) r = mid-1;
else l = mid;
}
事实上这个题目没有最小值的最大值或者最大值的最小值的概念
所以我们两个都可以使用
最重要的在于check函数
我们写出来代码后答案是:
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
long long w[N], v[N], qj1[N], qj2[N];
long long n, m, s, ml = 0x3f, mr;
long long ans = 0x3f3f3f3f3f3f3f3f, res;
bool check(int mid) {
res = 0;
long long a = 0, b = 0,c=0;
for (int j = 1; j <= m; j++) {
for (int i = qj1[j]; i <= qj2[j]; i++) {
if (w[i] >= mid) {
a++;
b += v[i];
}
}
c += a * b;
a = 0, b = 0;
}
res = llabs(c - s);
if (c > s) return true;
return false;
}
int main() {
cin >> n >> m >> s;
for (int i = 1; i <= n; i++) {
cin >> w[i] >> v[i];
ml = min(ml, w[i]);
mr = max(mr, w[i]);
}
for (int i = 1; i <= m; i++) {
cin >> qj1[i] >> qj2[i];
}
int l = ml - 1, r = mr + 2;
while (l < r) {
int mid = l + r >> 1;
if (check(mid)) l = mid + 1;
else r = mid;
if (ans > res) ans = res;
}
cout << ans;
}
在check函数里面我们每次判断mid的值来枚举所有的区间
最终:
可想而知有点超时,时间复杂度在 O(log2n*n*m)
那么如何优化? 前缀和!
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
long long w[N], v[N], qj1[N], qj2[N], qz1[N], qz2[N];
long long n, m, s, ml = 0x3f, mr;
long long ans = 0x3f3f3f3f3f3f3f3f, res;
bool check(int mid) {
res = 0;
long long c = 0;
memset(qz1, 0, sizeof qz1);
memset(qz2, 0, sizeof qz2);
for (int i = 1; i <= n; i++) {
if (w[i] >= mid) {
qz1[i] += 1;
qz2[i] += v[i];
}
qz1[i] += qz1[i - 1];
qz2[i] += qz2[i - 1];
}
for (int i = 1; i <= m; i++) {
c += (qz1[qj2[i]] - qz1[qj1[i] - 1]) * (qz2[qj2[i]] - qz2[qj1[i] - 1]);
}
res = llabs(c - s);
if (c > s) return true;
return false;
}
int main() {
cin >> n >> m >> s;
for (int i = 1; i <= n; i++) {
cin >> w[i] >> v[i];
ml = min(ml, w[i]);
mr = max(mr, w[i]);
}
for (int i = 1; i <= m; i++) {
cin >> qj1[i] >> qj2[i];
}
int l = ml - 1, r = mr + 2;
while (l < r) {
int mid = l + r >> 1;
if (check(mid)) l = mid + 1;
else r = mid;
if (ans > res) ans = res;
}
cout << ans;
}
完美AC!
最后总结一些点:
二分最重要的是在于判断是否单调!