1. 程式人生 > 其它 >AcWing 499. 聰明的質監員

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=j1jvj 。這公式說白了就是讓你求區間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(YS)最小。到這裡終於明白他要幹嘛了,題目的意思真是超級無敵好理解呢

二分

找這個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 YS時,說明mid有可能是解,所以令r = mid,(mid越大的話,Y就會越小,所有右半捨棄)
  • Y > S Y > S Y>S時,說明mid不會是解,令l = mid + 1。(這個說法在邊界處不成立,再看我後面的分析)

二分邊界:考慮區間長度只有2的情況,即r = l + 1時,記 [ l , r ] [l,r] [lr],計算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 $來計算,最優解的位置是rr + 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;
}