Loading

整体二分小记

没学整体二分前,大概知道这是把询问按答案分下去的做法,然而一直不清楚细节。

学了 P3332 后,再来好好写一下整体二分。

可能理解更深入后还会继续写?


P3332:

给定 \(n\) 个集合,要求支持区间加元素,求区间并集 \(k\) 小值。

整体二分的关键是同时用值域分离答案和询问。它的好处是可以直接按时间轴排序,要求是修改之间与判定答案互不干涉。

solve(ql, qr, sl, sr) 表示解决第 \(ql-qr\) 个操作中的问题,并已知道答案在 \([sl,sr]\)

其中有一步是要逐个判定答案是否在 \(mid\) 以下。

递归处理 \([mid+1,qr]\) 时,我们应当体现 \([ql,mid]\) 的影响。而这个影响可以体现在辅助数据结构中,或询问中。

如果在辅助数据结构中,重新划分这件事会打乱时间轴。所以只能将影响放到询问中,让区间的处理不依赖于其它操作。

需要想清楚的一点是,当我们进入一个区间的分治时,辅助数据结构应当是空的。因为我们已经把前面的操作的影响逐渐消去了,只用考虑这个操作区间的操作对这个区间的答案的影响。

其实,整体二分,就是把所有询问放一起二分。在带修改且有单调性的问题中,整体二分能最大体现其优越性。它保证了操作在时间上是连续的,而通过在往下划分区间时处理前面的所有影响的方式来处理修改对询问的影响。于是,递归到的区间是一个单独的子问题,解决它的复杂度与整个数据集无关。

#include <cstdio>
#define int long long
#define LL long long
using namespace std;
const int M = 50005;
struct opt{
    int op, l, r, c, id;
} q[M], pl[M], pr[M];
int n, ans[M], m;
struct segtr{
    int s[M << 2], laz[M << 2];
    void pushdown(int o, int l, int r){
        int mid = l + r >> 1;
        s[o<<1] += (mid-l+1) * laz[o]; s[o<<1|1] += (r-mid) * laz[o];
        laz[o<<1] += laz[o]; laz[o<<1|1] += laz[o];
        laz[o] = 0;
    }
    void modify(int o, int l, int r, int x, int y, int t){
        if(x <= l && r <= y) {s[o] += (r-l+1) * t; laz[o] += t; return;}
        int mid = l + r >> 1; pushdown(o, l, r);
        if(x <= mid) modify(o<<1, l, mid, x, y, t);
        if(y > mid) modify(o<<1|1, mid+1, r, x, y, t);
        s[o] = s[o<<1] + s[o<<1|1];
    }
    LL query(int o, int l, int r, int x, int y){
        if(x <= l && r <= y) return s[o];
        LL ans = 0ll, mid = l + r >> 1; pushdown(o, l, r);
        if(x <= mid) ans += query(o<<1, l, mid, x, y);
        if(y > mid) ans += query(o<<1|1, mid+1, r, x, y);
        return ans;
    }
} t;
// 处理编号 ql-qr 操作,确定答案在 sl-sr 之间
void solve(int ql, int qr, int sl, int sr){
    if(sl == sr) {
        for(int i = ql; i <= qr; i++) if(q[i].op == 2) ans[q[i].id] = sl;
        return;
    }
    int mid = sl + sr >> 1, cntl = 0, cntr = 0;
    for(int i = ql; i <= qr; i++){
        if(q[i].op == 1){
            if(q[i].c > mid) t.modify(1, 1, n, q[i].l, q[i].r, 1), pr[++cntr] = q[i];
            else pl[++cntl] = q[i];
        } else {
            int cnt = t.query(1, 1, n, q[i].l, q[i].r);
            if(cnt >= q[i].c) pr[++cntr] = q[i];
            else pl[++cntl] = q[i], pl[cntl].c -= cnt;
        }
    }
    for(int i = ql; i <= qr; i++) if(q[i].op == 1 && q[i].c > mid) t.modify(1, 1, n, q[i].l, q[i].r, -1);
    for(int i = 1; i <= cntl; i++) q[ql + i - 1] = pl[i];
    for(int i = 1; i <= cntr; i++) q[ql + i + cntl - 1] = pr[i];
    solve(ql, ql+cntl-1, sl, mid); solve(ql+cntl, qr, mid+1, sr);
}
signed main(){
    scanf("%lld %lld", &n, &m);
    for(int i = 1; i <= m; i++){
        scanf("%lld %lld %lld %lld", &q[i].op, &q[i].l, &q[i].r, &q[i].c);
        q[i].id = i;
    }
    solve(1, m, 0, n);
    for(int i = 1; i <= m; i++) if(ans[i]) printf("%lld\n", ans[i]);
}
posted @ 2022-08-19 15:37  purplevine  阅读(31)  评论(0编辑  收藏  举报