Even(23Nowcoder6.J)(二分+可持久化线段树)

题意:

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

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

\(n\le10^4, a_i\le 10^9, 1\le l\le r \le n,k\le 10^9\)

Solution:

\(Hint1\) 不管奇数还是偶数,操作次数始终是\(\log{a_i}\)次,所以一共能操作\(n * \log{a_i}\)

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

\(Hint3\) 考虑获取这个序列,优先队列模拟即可

\(Hint4\) 考虑任意选择区间,\(k=10^9\),容易知道此时的操作序列一定为\(Operations\)的一个子序列,令该序列为\(SubOperations\), 那随着\(k\)缩小,新的操作序列一定为\(SubOperations\)的一个前缀

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

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

时间复杂度\(O(qlog^2+n\log^2)\),空间复杂度\(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;
    }
};

int idx;
int root[maxn << 5], ls[maxn << 9], rs[maxn << 9], cnt[maxn << 9], Max[maxn << 9];

void build(int &rt, int l, int r) {
    rt = ++idx;
    if (l == r) {
        Max[rt] = a[l];
        return;
    }
    int mid = l + r >> 1;
    build(ls[rt], l, mid);
    build(rs[rt], mid + 1, r);
    Max[rt] = max(Max[ls[rt]], Max[rs[rt]]);
}

void update(int rtu, int &rtv, int l, int r, int pos, int val) {
    rtv = ++idx;
    ls[rtv] = ls[rtu], rs[rtv] = rs[rtu];
    cnt[rtv] = cnt[rtu]; Max[rtv] = Max[rtu];
    if (l == r) {
        Max[rtv] = (a[l] >>= 1);
        cnt[rtv] += 1;
        return ;
    }
    int mid = l + r >> 1;
    if (pos <= mid) update(ls[rtu], ls[rtv], l, mid, pos, val);
    else update(rs[rtu], rs[rtv], mid + 1, r, pos, val);
    cnt[rtv] = cnt[ls[rtv]] + cnt[rs[rtv]];
    Max[rtv] = max(Max[ls[rtv]], Max[rs[rtv]]);
}
int query( int rt, int l, int r, int ql, int qr) {
    if (ql <= l && r <= qr) return cnt[rt];
    int mid = l + r >> 1, res = 0;
    if (ql <= mid) res += query(ls[rt], l, mid, ql, qr);
    if (mid < qr) res += query(rs[rt], mid + 1, r, ql, qr);
    return res;
}
int query_Max(int rt, int l, int r, int ql, int qr) {
    if (ql <= l && r <= qr) return Max[rt];
    int mid = l + r >> 1, res = 0;
    if (ql <= mid) res = max(query_Max(ls[rt], l, mid, ql, qr), res);
    if (mid < qr) res = max(query_Max(rs[rt], mid + 1, r, ql, qr), res);
    return res;
}
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';
    }
}
posted @ 2023-08-04 22:15  Fighoh  阅读(49)  评论(0编辑  收藏  举报