Educational Codeforces Round 53 (Rated for Div. 2) D. Berland Fair 二分+樹狀陣列 O(n*logn*logn) 思路

ACVoyager發表於2018-11-03

題意:

給定n個店鋪,每個店鋪買的東西有個價格a_i,數量有無限個,然後主人公從1號開始走到n號,每走到一個店鋪,只要他的錢大於價格,他就要買,然後重複上述過程,直到他不能購買,輸出他能買的物品件數;

思路:

直接模擬的話,必然不可行,但是我們知道模擬時到達一個位置後買不起這裡的東西就可以把這個店鋪給踢了(好壞!),然後繼續後面的過程,如果要是我的錢足夠買完一輪的話,我會除一下,取下膜,這樣就能快速知道購買的個數,如果不能買完一輪,說明又有我買不起的東西了,然後就要把他踢走;

如上述過程所述,我們應該怎麼高效維護這個過程呢,如果我們知道其中有我們買不起的東西,我們要快速的找到他的位置,顯然遍歷是不行的,這時候我們就會想到預處理字首和,然後此時整個序列就是有序的了,然後可以二分查詢第一個買不起的位置;

但是找到她以後我還要綁了她,所以我還要一個能維護字首和並支援修改的資料結構,顯然是樹狀陣列;

那這樣解題過程就是每次找一個我買不起的位置,刪去,然後計算這一輪我能買多少個,知道所有的我都買不起了(全踢出去了,n_為0),輸出答案;

 

思考:有的題或許不能一上來就得到正確的思路,這時我們可以先從暴力的角度考慮,然後發現一些問題以及可優化的點,然後運用其他方式優化掉;正如這個C題https://blog.csdn.net/xiang_6/article/details/83686299,一開始也是想到的列舉區間,但是顯然不行,所以我們去二分這個長度然後check,

 

#include<bits/stdc++.h>

using namespace std;

#define out fflush(stdout)
#define fast ios::sync_with_stdio(0),cin.tie(0);

#define FI first
#define SE second

typedef long long ll;
typedef pair<int,int> P;

const int maxn = 2e5 + 7;
const int INF = 0x3f3f3f3f;

ll c[maxn+7];

void add(int id, ll v) {
    for(int i = id; i <= maxn; i += (i&-i)) {
        c[i] += v;
    }
}
ll sum(ll id) {
    ll res = 0;
    for(int i = id; i > 0; i -= (i&-i)) {
        res += c[i];
    }
    return res;
}

int n;
ll m;
ll a[maxn];

int main() {
    scanf("%d%lld", &n, &m);
    for(int i = 1; i <= n; ++i) {
        scanf("%lld", &a[i]);
        add(i, a[i]);
    }
    int n_ = n;
    ll ans = 0;
    while(1) {
        int l = 1, r = n;
        int pos = -1;
        while(l <= r) {
            int mid = (l + r) >> 1;
            ll t = sum(mid);
            if(t > m) {
                pos = mid;
                r = mid - 1;
            }
            else {
                l = mid + 1;
            }
        }
        if(pos == -1) {
            ll t = sum(n);
            ans += (m / t)*n_;
            m %= t;
        }
        else {
            add(pos, -a[pos]);
            n_--;
        }
        if(n_ == 0) break;
    }
    cout << ans << endl;
    return 0;
}

 

相關文章