题意
给出nnn个东西以及他们的重量和价值。计算这些矿石的检验值:
1、给出mmm个区间[li,ri][l_i,r_i][li,ri]。
2、确定一个参数WWW。
3、对于一个区间[li,ri][l_i,r_i][li,ri],计算矿石在这个区间上的检验值YiY_iYi:这个区间上所有重量大于等于WWW的矿石数目与它们的价值和的乘积。这批矿产的检验结果YYY为各个区间的检验值之和。
我们要调整这个参数使得检验结果与给出的标准值SSS相差尽量小。
思路
如果调整WWW,调高会使有些矿石无法选择,答案就会变小,调低就会使答案变大。
我们发现检验值具有单调性,然后可以愉快的想到二分。
因为WWW一定是这些矿石中的某个重量,所以我们可以利用这些矿石的重量进行二分。
对于二分到的答案,如果大于SSS,就把WWW调高,否则调小,不断逼近SSS,然后我们在每次求出的检验结果中取最小值。
对于求检验结果,朴素算法是枚举每个区间,再暴力判断,这样时间复杂度为O(N2)O(N^2)O(N2),显然会超时。我们可以利用前缀和的方法,把超过WWW的矿石的信息都加到前缀和里,这样子可以优化成O(2N)O(2N)O(2N)。
最后时间复杂度为O((N+M)logN)O((N+M)logN)O((N+M)logN)。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
struct node{
int w, v;
}stone[200001];
int n, m;
int a[200001], l[200001], r[200001], pre_n[200001];
long long ans, s;
long long pre_v[200001];
long long check(int x) {
for (int i = 1; i <= n; i++) {
pre_v[i] = pre_v[i - 1] + (stone[i].w >= x ? stone[i].v : 0);
pre_n[i] = pre_n[i - 1] + (stone[i].w >= x ? 1 : 0);
}//利用前缀和求检验结果
long long y = -s;
for (int i = 1; i <= m; i++)
y += (pre_v[r[i]] - pre_v[l[i] - 1]) * (pre_n[r[i]] - pre_n[l[i] - 1]);
return y;
}
int main() {
scanf("%d %d %lld", &n, &m, &s);
for (int i = 1; i <= n; i++) {
scanf("%d %d", &stone[i].w, &stone[i].v);
a[i] = stone[i].w;
}
for (int i = 1; i <= m; i++)
scanf("%d %d", &l[i], &r[i]);
std::sort(a + 1, a + n + 1);
int t = std::unique(a + 1, a + n + 1) - a - 1;
int l1 = 1, r1 = t + 1, mid;
ans = 1e18;
while (l1 < r1) {
mid = (l1 + r1) >> 1;
long long y = check(a[mid]);
if (y > 0) l1 = mid + 1;//往S逼近
else r1 = mid, y = -y;
ans = std::min(ans, y);
}
printf("%lld", ans);
}
本文介绍了一种针对矿石检验值的优化算法,通过二分查找和前缀和技巧,实现了对参数的精确调整,以使矿石检验结果与标准值的差距最小。算法时间复杂度为O((N+M)logN),适用于处理大规模数据集。
390

被折叠的 条评论
为什么被折叠?



