整体二分

动态第k大,第k小,离线下来。

考虑第k小怎么求,二分一个数值,然后扫一遍有多少小于等于 \(mid\) 的。一次nlogn,多次\(n^2log\),既然你的是我的,我的就是你的,你做的就是我做的,我做的就是你做的,如果我做了,你又做,不就冗余了吗,那么就能一起二分,这样就是\(nlogn\)。怎么一起,还是枚举一个值,然后扫一下序列,有cnt个小于等于mid,把k小于等于cnt划分到左边,其余的划分到右边,分治,用线段树分析一波,就是\(nlogn\)。注意如果[l,mid],[mid + 1,r]有一个为空了,及时return,保证时间复杂度。就好像线段树单点修改本来是log的,如果左右儿子都扫,变成nlogn。

动态,就是多了一个修改,添加。

点击查看代码
void solve(int l,int r,int L,int R){
    if(L > R)return;
    if(l == r){
        for(int i = L;i <= R;++i)if(q[i].opt == 1)ans[q[i].id] = l;
        return;
    }
    int mid = l + r >> 1,ed1 = 0,ed2 = 0;
    for(int i = L;i <= R;++i){
        if(q[i].opt == 0){
            if(q[i].l <= mid){
                T.add(q[i].r,q[i].k);
                q1[++ed1] = q[i];
            }else q2[++ed2] = q[i];
        }else{
            int t = T.ask(q[i].r) - T.ask(q[i].l - 1);
            if(q[i].k <= t){
                q1[++ed1] = q[i];
            }else q[i].k -= t,q2[++ed2] = q[i];
        }
    }
    for(int i = 1;i <= ed1;++i)if(q1[i].opt == 0)T.add(q1[i].r,-q1[i].k);
    for(int i = 1;i <= ed1;++i)q[L + i - 1] = q1[i];
    for(int i = 1;i <= ed2;++i)q[L + ed1 + i - 1] = q2[i];
    solve(l,mid,L,L + ed1 - 1);
    solve(mid + 1,r,L + ed1,R);
}
posted @ 2022-11-20 20:07  zasdcn  阅读(55)  评论(1编辑  收藏  举报