天天看點

21.CF840D Destiny 可持久化權值線段樹+二分查詢

21.CF840D Destiny 可持久化權值線段樹+二分查詢

個人Limitの線段樹題單題解主目錄:

給定個元素,次詢問。

每次給出三個參數,詢問區間内是否存在出現次數嚴格大于的數。如果存在就輸出最小的那個,否則輸出

區間大于的數,于是想到用可持久化權值線段樹解決,注意額外檢查左右區間。

洛谷傳送門:​​CF840D Destiny - 洛谷 | 計算機科學教育新生态 (luogu.com.cn)​​

CF傳送門:​​D. Destiny (codeforces.com)​​

題目分析

做多了會發現,這種題的重點在于樹上二分查詢的邊界條件及左右子樹遞歸的政策。

Code

#include <bits/stdc++.h>
#define ll long long
#define int long long
using namespace std;

const int N = 5e5 + 10;

ll a[N], b[N];
ll tot, root[N << 5], sum[N << 5], lc[N << 5], rc[N << 5];

void update(int &rt, int pre, int l, int r, int k, int v){
    rt = ++tot, lc[rt] = lc[pre], rc[rt] = rc[pre], sum[rt] = sum[pre];
    if(l == r){
        sum[rt] += v;
        return;
    }
    int mid = l + r >> 1;
    if(k <= mid) update(lc[rt], lc[pre], l, mid, k, v);
    else update(rc[rt], rc[pre], mid + 1, r, k, v);
    sum[rt] = sum[lc[rt]] + sum[rc[rt]];
}

int query(int rt, int pre, int l, int r, int k){
    if(sum[rt] - sum[pre] <= k) return -1;
    if(l == r) return l;
    ll res = 1e18;
    ll mid = l + r >> 1;
    if(sum[lc[rt]] - sum[lc[pre]] > k){
        ll ans = query(lc[rt], lc[pre], l, mid, k);
        if(ans > 0) res = min(res, ans);
    }
    if(sum[rc[rt]] - sum[rc[pre]] > k){
        ll ans = query(rc[rt], rc[pre], mid + 1, r, k);
        if(ans > 0) res = min(res, ans);
    }
    return res;
}

signed main(){
    ios_base::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    ll n, m; cin >> n >> m;
    for(int i = 1; i <= n; i++) cin >> a[i];
    for(int i = 1; i <= n; i++) update(root[i], root[i - 1], 1, n, a[i], 1);
    while(m--){
        ll l, r, k; cin >> l >> r >> k;
        ll tar = (r - l + 1) / k, ans = query(root[r], root[l - 1], 1, n, tar);
        if(ans == 1e18) cout << -1 << endl;
        else cout << ans << endl;
    }
    return 0;
}      

繼續閱讀