1. 程式人生 > >Educational Codeforces Round 53 (Rated for Div. 2)D. Berland Fair(貪心)

Educational Codeforces Round 53 (Rated for Div. 2)D. Berland Fair(貪心)

題目連結

題意

n n 個物品圍成一圈,每個物品都有一個價值 a i a_i ,現在有 T

T 元錢,現在從第一個物品開始順時針往後買,問在滿足下列條件下能買多少個物品。

  1. 如果當前剩餘的錢大於等於當前物品的價值,那就必須買下這個物品。
  2. 如果當前剩餘的錢不足以買下這個物品,則跳過。
  3. 如果當前的錢不足以買任何物品,則結束。

資料範圍
1 n

2 × 1 0 5 1 T
1 0 18 , 1 a i 1 0 9 1\leq n \leq 2\times10^5,1 \leq T \leq 10^{18},1\leq a_i \leq 10^9

題解

因為T太大了,如果暴力迴圈購買肯定超時。超時的原因就是當一圈可以多次進行購買時,沒必要每個物品都迴圈過去。例如 T = 20 T = 20 , 5個物品價值為 1 , 1 , 1 , 1 , 21 1, 1, 1 ,1,21 ,那直接 20 4 × 4 \frac{20}{4}\times 4 即可。關鍵是如何得出當前的T和當前能買的價值總和。

用一個大根堆 q u e que 儲存物品價值和 s u m sum 儲存當前價值總和。
如果 T < s u m T < sum 同時 q u e . t o p ( ) > T que.top() > T ,那麼肯定有一個物品的價值已經大於了當前剩餘的錢數,這個物品就可以丟掉不用考慮了。否則的話可能有兩種情況:

  1. 剩餘物品的單個價格都小於等於T,但是總和大於T。
  2. 剩餘物品的單個價格和總和都小於等於T。

對於情況1要遍歷全部物品,對於情況2直接加減乘除就好。
不斷重複上述步驟,直到物品總和為0。
大概為O(nlogT) 時間複雜度O(能過)。菜雞根本不會分析…

程式碼

#include <iostream>
#include <queue>
#include <cstdio>
using namespace std;
const int maxn = 2e5+5;
typedef long long ll;
ll n,t,cnt,a[maxn];
int main() {
    scanf("%lld%lld", &n,&t);
    ll sum = 0;
    ll total = n;
    priority_queue<ll> que;
    for(int i = 0; i < n; ++i) {
        scanf("%lld", &a[i]);
        que.push(a[i]);
        sum += a[i];
    }
    while(1) {
        while(sum > t && !que.empty() && que.top() > t) {
            sum -= que.top();
            que.pop();
            total--;
        }
        if(sum == 0) break;
        cnt += t/sum*total;
        t %= sum;
        for(int i = 0; i < n; ++i)
            if(t >= a[i])
                t -= a[i],cnt++;
    }
    printf("%lld\n", cnt);
    return 0;
}