题目链接
关于套路
涉及到区间的,想都不用想,前缀和。然后再涉及一个找xxx的最小值的,想都不用想,二分。所以这题就是一道前缀和+二分的题。但是题目在说啥我好像没看明白?
题意
题意是这样的:个你n
个产品的质量w
和价值v
,依次编号1-n
然后再给你m个查询l
,r
,其中l
是最左边的产品编号,r
是最右边产品的编号。比如产品编号有1,2,3,4,5。如果[l,r] = [1,5]的话,就是让你从编号1-5的产品中找满足某个条件的产品出来
再来解释一下
Y
i
=
∑
j
1
∑
j
v
j
Y_i= \sum_j1 \sum_jv_j
Yi=∑j1∑jvj 。这公式说白了就是让你求区间i
内,先找出所有满足w >= W的产品,最后
Y
i
Y_i
Yi =产品个数
∗
*
∗ 这些产品的总价值。最后统计所有区间的
Y
i
Y_i
Yi之和
Y
Y
Y,使得
a
b
s
(
Y
−
S
)
abs(Y - S)
abs(Y−S)最小。到这里终于明白他要干嘛了,题目的意思真是超级无敌好理解呢
二分
找这个W,按照套路一般是二分,题目给的w范围是 1 < = w < = 1 0 6 1 <= w <=10^6 1<=w<=106,那么二分的初始范围就是 [ 0 , 1 0 6 + 1 ] [0, 10^6+1] [0,106+1],(1和 1 0 6 10^6 106有可能都不是解)
再来考虑边界如何收缩,记当前左边界为l
,右边界为r
,
m
i
d
=
(
l
+
r
)
/
2
mid = (l + r ) / 2
mid=(l+r)/2,假设W为mid。
-
Y
≤
S
Y \leq S
Y≤S时,说明mid有可能是解,所以令
r = mid
,(mid越大的话,Y就会越小,所有右半舍弃) -
Y
>
S
Y > S
Y>S时,说明mid不会是解,令
l = mid + 1
。(这个说法在边界处不成立,再看我后面的分析)
二分边界:考虑区间长度只有2的情况,即r = l + 1
时,记
[
l
,
r
]
[l,r]
[l,r],计算Y值的函数为getY,
m
i
d
=
(
l
+
r
)
/
2
=
l
mid = (l + r ) / 2 = l
mid=(l+r)/2=l
假设到了最后,getY(l)大于S,getY( r )小于S。这时最优解的位置为l
或者r
最优解在r
的位置上的话 由于getY(mid) > S, 最终会令l = mid + 1 = 2结束二分, r
正好是最优解
最优解在l
的位置上的话 由于getY(mid) > S, 最终也是l = mid + 1 = 2结束,r-1
才是最优解
所以最优解的位置是r
或者r - 1
PS: 如果用$mid = (l + r + 1) / 2 $来计算,最优解的位置是r
或r + 1
,可以自行推导哦
前缀和
对于每次二分,先用前缀和处理一下满足条件产品的个数和价值,最后用前缀和公式就可快速得到每个区间的 Y i Y_i Yi,累加就可得到Y,详情请看我的代码啦
代码
#include<iostream>
using namespace std;
typedef pair<int,int> PII;
typedef long long LL;
const int N = 2e5 + 10, M = 1e6 + 10;
int w[N], v[N], cnt[N];
PII q[N];
LL s[N];
int n, m;
LL S;
LL getY(int mid)
{
//cnt[]计算产品个数,s[]计算产品的价值和
for(int i = 1; i <= n; i++)
{
if(w[i] >= mid)
{
s[i] = v[i], cnt[i] = 1;
}
else{
s[i] = 0, cnt[i] = 0;
}
s[i] += s[i - 1];
cnt[i] += cnt[i - 1];
}
LL Y = 0;
for(int i = 0; i < m; i++)
{
int x = q[i].first, y = q[i].second;
Y += (cnt[y] - cnt[x - 1]) * (s[y] - s[x - 1]);
}
return Y;
}
int main()
{
scanf("%d%d%lld", &n, &m, &S);
for(int i = 1; i <= n; i++) scanf("%d%d", &w[i], &v[i]);
for(int i = 0; i < m; i++)
{
int x, y;
scanf("%d%d", &x, &y);
q[i] = {x, y};
}
int l = 0, r = 1e6 + 1;
while(l < r)
{
int mid = l + r >> 1;
if(getY(mid) <= S) r = mid;
else l = mid + 1;
}
printf("%lld", min(abs(S - getY(r)), abs(S - getY(r - 1))));
return 0;
}