算法 | 青训营

81 阅读2分钟

题意:

给定一个序列aa,定义一次操作选择序列中一个元素a[i]a[i], 使ai=ai2a_i = \lfloor \frac{a_i}{2} \rfloor,其中aia_i为当前序列中的最大偶数,若没有则是最大奇数。

qq组询问,每次给定k,l,rk, l, r分别表示操作次数和操作区间,每次回答操作完成后区间中的MaxMax,询问间互相独立。

n104,ai109,1lrn,k109n\le10^4, a_i\le 10^9, 1\le l\le r \le n,k\le 10^9

Solution:

Hint1Hint1 不管奇数还是偶数,操作次数始终是logai\log{a_i}次,所以一共能操作nlogain * \log{a_i}

Hint2Hint2 考虑一次询问的k=109k=10^9,区间为[1,n][1,n]次的情况,如果那么每次一操作的元素下标是一定的,令该序列为OperationsOperations

Hint3Hint3 考虑获取这个序列,优先队列模拟即可

Hint4Hint4 考虑任意选择区间,k=109k=10^9,容易知道此时的操作序列一定为OperationsOperations的一个子序列,令该序列为SubOperationsSubOperations, 那随着kk缩小,新的操作序列一定为SubOperationsSubOperations的一个前缀

Hint5Hint5 考虑以建立Operations|Operations|棵线段树,每棵线段树内部维护信息为cntMaxcnt和Max,内部下标为[1,n][1, n],那么第ii棵线段树中cntcnt记录执行到第ii次操作时对区间的操作次数,那么MaxMax就是维护一个区间最大值,每次操作都是单点修改,只会改变一条链,故建立可持久化线段树。

Hint6Hint6 考虑解决询问,我们只要每次二分这Operations|Operations|个版本,找到第一个区间内操作次数k\ge k的版本即可,然后输出区间最大值即为答案

时间复杂度O(qlog2+nlog2)O(qlog^2+n\log^2),空间复杂度O(nlog2)O(nlog^2)

比题解低能,但是思想简单,写法简单,但是没写出来🤡

Code:

int n, q;
int a[maxn];
struct node {
    int x, id;
    bool operator<(const node &t) const {
        if (t.x % 2 != x % 2) return x % 2 > t.x % 2;
        if (x != t.x) return x < t.x;
        return id > t.id;
    }
};


priority_queue<node> Q;
void solve(int cas) {
    cin >> n >> q;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
        if (a[i]) Q.push((node){a[i], i});
    }
    build(root[0], 1, n);
    int cnt = 0;
    while (!Q.empty()) {
        node p = Q.top(); Q.pop();
        auto [x, id] = p;
        update(root[cnt], root[cnt + 1], 1, n, id, 1);
        cnt++;
        if (x / 2 > 0) Q.push({x >>= 1, id});
    }
    for (int i = 1; i <= q; ++i) {
        int l, r, k; cin >> l >> r >> k;
        int L = 1, R = cnt, t = 0;
        while (L <= R) {
            int mid = L + R >> 1;
            if (query(root[mid], 1, n, l, r) <= k) {
                t = mid;
                L = mid + 1;
            } else R = mid - 1;
        }
        cout << query_Max(root[t], 1, n, l, r) << '\n';
    }
}