P1314. 聪明的质监员

考点

  • 二分
  • 前缀和

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 50;
int n, m;
// wv前缀和 有效重量的个数和
// vv前缀和 有效重量的价值和
ll s, wv[maxn], vv[maxn];

// 存储矿石信息
struct node {
int w_, v_;
} a[maxn];

// 存储区间信息
struct range {
int l_, r_;
} b[maxn];

ll calc(int mid) {
memset(wv, 0, sizeof(wv)), memset(vv, 0, sizeof(vv));
ll sum = 0;
for (int i = 1; i <= n; ++i) {
if (a[i].w_ >= mid) {
wv[i] += 1, vv[i] += a[i].v_;
}
wv[i] += wv[i - 1], vv[i] += vv[i - 1];
}
for (int i = 1; i <= m; ++i) {
sum += (wv[b[i].r_] - wv[b[i].l_ - 1]) * (vv[b[i].r_] - vv[b[i].l_ - 1]);
}
return sum;
}

int main() {
cin >> n >> m >> s;
int l = 0, r = 0;
for (int i = 1; i <= n; ++i) cin >> a[i].w_ >> a[i].v_, r = max(r, a[i].w_);
++r;
for (int i = 1; i <= m; ++i) cin >> b[i].l_ >> b[i].r_;
ll ans = LLONG_MAX;
while (l <= r) {
int mid = (l + r) / 2;
ll y = calc(mid);
ans = min(ans, abs(s - y));
if (y == s) {
break;
} else if (y > s) {
l = mid + 1;
} else {
r = mid - 1;
}
}
cout << ans;
return 0;
}

思路

根据题意,每个区间内都有:

Y = 合法个数乘上合法价值和

而要我们求的y就等于每个区间的Y之和。

显然区间合法个数区间合法价值和是满足容斥原理的,可以使用前缀和进行优化。


由于要让y尽可能地接近s,发现结果具有单调性,可以进行答案二分:

  • 标准值W越大,那么合法的矿石就少,y就小;
  • 标准值W越小,那么合法的矿石就多,y就大;
  • y小于s时,说明y应该变大,这样才能接近s,也就是让W变小;
  • y大于s时,说明y应该变小,这样才能接近s,也就是让W变大。

W的二分范围等于[0, 最大矿石重量 + 1]

左边界是所有矿石都满足条件时,右边界是所有矿石都不满足条件时;这样就不会遗漏情况。