AcWing 499. 聰明的質監員
技術標籤:演算法提高課
題目連結
關於套路
涉及到區間的,想都不用想,字首和。然後再涉及一個找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範圍是
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
再來考慮邊界如何收縮,記當前左邊界為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
假設到了最後,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;
}