洛谷 P1314 聪明的质监员
扯
题号挺不错/xyx
用暴力 + 吸氧过了 \(75\) 分,然后用玄学优化,不知道为啥一直就 \(5\) 分、 \(10\) 分了,看来我还是太菜了
思路
二分答案+前缀和
显然计算一次 \(y=\sum\limits_{i=1}^{m}y_i\)可以用前缀和 \(O(n)\) 算出来。
现在要考虑的是如何快速找到一个 \(W\),能使 \(|s-y|\) 的值最小,由于计算检验值之和已经是 \(O(n)\) 的复杂度了,所以在这样的情况下最多允许找 \(O(\log n)\) 次 \(W\) 的取值,这样代码的总复杂度是 \(O(n\log n)\) 的,是能过这道题的。
因为 \(W\) 越大,检验值的和 \(y\) 就越小,所以检验值的和是单调的,因此就可以想到二分答案,刚好复杂度就是 \(O(n\log n)\) 的,我们就找到了一种复合复杂度的做法。
接下来确定左右端点以及二分判定条件:
- 左右端点显然可以用序列最小值和序列最大值
- 在上面已经说过了,\(W\) 越大,\(y\) 就越小,所以判定条件就出来了
- 如果 \(W=mid\) 时的权值 \(y > s\),那么\(l = mid +1\)
- 否则 \(r = mid - 1\)
- 当 \(|s-y| < ans\) 时,更新 \(ans=|s-y|\)
这样就做完了
代码
/*
Name: P1314 聪明的质监员
Author: Loceaner
Date: 30/08/20 18:05
Description: 二分答案
Debug: 数据范围
*/
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
using namespace std;
const int A = 2e5 + 11;
const int B = 1e6 + 11;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
inline int read() {
char c = getchar();
int x = 0, f = 1;
for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
return x * f;
}
inline ll lread() {
char c = getchar();
ll x = 0, f = 1;
for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
return x * f;
}
int n, m, L[A], R[A];
ll s, val1[A], val2[A], w[A], v[A], maxn, minn;
ll calc(ll x) {
ll sum = 0;
for (int i = 1; i <= n; i++) {
val1[i] = val1[i - 1];
val2[i] = val2[i - 1];
if (w[i] >= x) val1[i]++, val2[i] += v[i];
}
for (int i = 1; i <= m; i++)
sum += (val1[R[i]] - val1[L[i] - 1]) * (val2[R[i]] - val2[L[i] - 1]);
return sum;
}
int main() {
n = read(), m = read(), s = lread();
for (int i = 1; i <= n; i++)
w[i] = lread(), v[i] = lread(),
maxn = max(maxn, w[i]), minn = min(minn, w[i]);
for (int i = 1; i <= m; i++)
L[i] = read(), R[i] = read();
ll l = minn - 1, r = maxn + 1, ans = 1e12;
while (l <= r) {
ll mid = (l + r) >> 1;
ll now = calc(mid);
if (abs(s - now) <= ans) ans = abs(s - now);
if (now > s) l = mid + 1;
else r = mid - 1;
}
cout << ans << '\n';
return 0;
}
转载不必联系作者,但请声明出处