CF799E Aquarium decoration

ddxrS發表於2024-11-24

我們將物品分成 \(4\) 個集合:

  • 集合 \(A\) 包含只有 Arkady 喜歡的裝飾。
  • 集合 \(B\) 包含只有 Masha 喜歡的裝飾。
  • 集合 \(C\) 包含二人都喜歡的裝飾。
  • 集合 \(D\) 包含沒人喜歡的裝飾。

分別記這 \(4\) 個集合的大小為 \(a,b,c,d\)

我們可以先將這 \(4\) 個集合分別按照花費從小到大來排序,並在後面按照這樣的順序來選擇裝飾。

不妨讓我們來列舉我們從集合 \(C\) 中選取了多少個裝飾,假設選擇了 \(i\) 個。

那麼我們至少要從集合 \(A\) 和集合 \(B\) 中分別選取 \(j=k-i\) 個裝飾。

那麼對於確定的 \(i\),這 \(i+2j\) 個裝飾是必選的,我們先將它們選上。此時還有一個限制沒有滿足,就是 \(i+2j\) 可能不足 \(m\)

此時,我們就需要從集合 \(A\) 的剩下 \(a-i\),集合 \(B\) 的剩下 \(b-j\),集合 \(C\) 的剩下 \(c-j\) 和集合 \(D\)\(d\) 個裝飾中選花費最小的 \(m-i-2j\) 個裝飾。

考慮用一棵權值線段樹來維護。我們將剩下的裝飾以花費作為下標新增的線段樹上,並維護區間的花費和以及擁有裝飾的數量。

每次相當於線上段樹上二分找到第一個滿足裝飾數量字首和大於等於 \(m-i-2j\) 的節點。

時間複雜度 \(O(n\log V)\),可以透過離散化做到 \(O(n\log n)\)

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N = 2e5 + 5, M = 1e9;
int n, m, qwq, K, c[N], a[N], b[N];
struct node {
    int t, id;
    bool operator <(const node &p) const {return t < p.t;}
} arr[N], brr[N], crr[N];
int cnta, cntb, cntc;
ll suma[N], sumb[N], sumc[N], ans = 0x3f3f3f3f3f3f3f3f;
int rot, siz[N << 5], lc[N << 5], rc[N << 5], cnt;;
ll tr[N << 5];
void change(int &p, int l, int r, ll x, int opt) {
    if(!p) p = ++cnt;
    int mid = l + r >> 1;
    if(l == r) return tr[p] += x * opt, siz[p] += opt, void();
    if(x <= mid) change(lc[p], l, mid, x, opt);
    else if(mid < x) change(rc[p], mid + 1, r, x, opt);
    tr[p] = tr[lc[p]] + tr[rc[p]], siz[p] = siz[lc[p]] + siz[rc[p]];
}
ll query(int p, int l, int r, int k) {
    int mid = l + r >> 1;
    if(l == r) return 1ll * k * l;
    if(siz[lc[p]] >= k) return query(lc[p], l, mid, k);
    else return tr[lc[p]] + query(rc[p], mid + 1, r, k - siz[lc[p]]);
}
int main() {
#ifdef ddxrS
    freopen("sample.in", "r", stdin);
    freopen("sample.out", "w", stdout);
#endif
    cin>>n>>m>>K;
    for(int i = 1; i <= n; i++) cin>>c[i];
    cin>>qwq; for(int i = 1, p; i <= qwq; i++) cin>>p, a[p] = true;
    cin>>qwq; for(int i = 1, p; i <= qwq; i++) cin>>p, b[p] = true;
    for(int i = 1; i <= n; i++) {
        if(a[i] && b[i]) crr[++cntc] = {c[i], i};
        else if(a[i]) arr[++cnta] = {c[i], i};
        else if(b[i]) brr[++cntb] = {c[i], i};
        else change(rot, 1, M, c[i], 1);
    }
    sort(arr + 1, arr + cnta + 1), sort(brr + 1, brr + cntb + 1), sort(crr + 1, crr + cntc + 1);
    for(int i = 1; i <= cnta; i++) suma[i] = suma[i - 1] + arr[i].t;
    for(int i = 1; i <= cntb; i++) sumb[i] = sumb[i - 1] + brr[i].t;
    for(int i = 1; i <= cntc; i++) sumc[i] = sumc[i - 1] + crr[i].t;
    for(int i = K + 1; i <= cnta; i++) change(rot, 1, M, arr[i].t, 1);
    for(int i = K + 1; i <= cntb; i++) change(rot, 1, M, brr[i].t, 1);
    for(int i = 1; i <= cntc; i++) change(rot, 1, M, crr[i].t, 1);
    for(int i = 0, j = min(cnta, K), k = min(cntb, K); i <= cntc && i <= K; i++) {
        while(j >= 1 && j > K - i) change(rot, 1, M, arr[j].t, 1), j--;
        while(k >= 1 && k > K - i) change(rot, 1, M, brr[k].t, 1), k--;
        if(i >= 1) change(rot, 1, M, crr[i].t, -1);
        if(j >= K - i && k >= K - i && i + j + k <= m && siz[rot] >= m - i - j - k) ans = min(ans, suma[j] + sumb[k] + sumc[i] + query(rot, 1, M, m - i - j - k));
    }
    cout<<(ans < 0x3f3f3f3f3f3f3f3f ? ans : -1)<<'\n';
    cerr<<clock() * 1.0 / CLOCKS_PER_SEC<<'\n';
    return 0;
}