整体二分

算法理解

建议结合OIwiki和本人文字食用

本质上离线询问,然后对询问的答案进行二分,根据二分的结果(满足?/不满足?)对问题根据询问进行分治(类似于CDQ分治?),即对于所有的询问同时进行二分

T1:

考虑单个问题 \((x,y,k)\)\(check\)

我们可以在序列的值域 \([l,r]\) 上二分, 然后判断在 \([l,r]\) 中数的序号在 \([x,y]\) 中的个数(大于?/小于?)k

可以用树状数组维护序号

多个问题怎么办?

多个问题全都再重新加一遍树状数组显然不优,那我们就同时进行所有询问的二分,把二分到同一个值域区间 \([l,r]\) 的询问同时进行树状数组查询,然后再根据二分结果进行分治递归

因为值域递归并且每一层都会递归到,递归深度为 \(O(log(值域))\) 并且再加上树状数组复杂度,过不了

可以剪枝水过

if(q1.size())  solve(l,mid,a1,q1);
if(q2.size())  solve(mid+1,r,a2,q2);

正确优化方法:离散化值域

总复杂度 \(O((n+m)log^2n)\)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
struct num{
    int x,p;
};
struct query{
    int x,y,k,i;
    void change(int x){
        k-=x;
    }
};
int n,m,mxnum;
int tr[N],ans[N],c[N],b[N];
map<int,int>mp;
int lowbit(int x){
    return x&(-x);
}
void merge(int x,int z){
    for(;x<=n;x+=lowbit(x)){
        tr[x]+=z;
    }
}
int ask(int x){
    int res=0;
    for(;x;x-=lowbit(x)){
        res+=tr[x];
    }
    return res;
}
void solve(int l,int r,vector<num>a,vector<query>q){
    if(l==r){
        for(auto i:q)  ans[i.i]=l;
        return;
    }
    int mid=(l+r)>>1;
    vector<num>a1,a2;
    for(auto i:a){
        if(i.x<=mid){
            a1.push_back(i);
            merge(i.p,1);
        }
        else{
            a2.push_back(i);
        }
    }
    vector<query>q1,q2;
    for(auto i:q){
        int res=ask(i.y)-ask(i.x-1);
        // printf("%d %d %d %d %d %d\n",i.x,i.y,i.k,res,l,r);
        if(i.k<=res)  q1.push_back(i);
        else  i.change(res),q2.push_back(i);
    }
    for(auto i:a){
        if(i.x<=mid){
            merge(i.p,-1);
        }
    }
    solve(l,mid,a1,q1);
    solve(mid+1,r,a2,q2);
}
int main(){
    scanf("%d%d",&n,&m);
    vector<num>a;
    vector<query>q;
    for(int i=1;i<=n;i++){
        scanf("%d",&c[i]);
        b[i]=c[i];
    }
    sort(b+1,b+1+n);
    int len=unique(b+1,b+1+n)-b-1;
    for(int i=1;i<=len;i++){
        mp[b[i]]=i;
    }
    for(int i=1;i<=n;i++){
        c[i]=mp[c[i]];
        a.push_back({c[i],i});
    }
    for(int i=1;i<=m;i++){
        int l,r,k;
        scanf("%d%d%d",&l,&r,&k);
        q.push_back({l,r,k,i});
    }
    solve(1,n,a,q);
    for(int i=1;i<=m;i++){
        printf("%d\n",b[ans[i]]);
    }
}

T2:

切掉了,其实你要是理解了上面这题,真的不难

因为整体二分的每一个询问是独立的,所以可以调换顺序,所以我们就按照时间顺序来进行判定,然后把修改操作变为先删再加的两个操作,同样按照时间顺序叉进来即可

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
struct num{
    int x,p;
};
struct change{
    int x,p,t,op;
};
struct query{
    int x,y,k,i,t;
    void change(int x){
        k-=x;
    }
};
int n,m,mxnum,cnt;
int tr[N],ans[N],s[N];
int lowbit(int x){
    return x&(-x);
}
void merge(int x,int z){
    for(;x<=n;x+=lowbit(x)){
        tr[x]+=z;
    }
}
int ask(int x){
    int res=0;
    for(;x;x-=lowbit(x)){
        res+=tr[x];
    }
    return res;
}
void solve(int l,int r,vector<num>a,vector<query>q,vector<change>ch){
    if(l==r){
        for(auto i:q)  ans[i.i]=l;
        return;
    }
    int mid=(l+r)>>1;
    vector<num>a1,a2;
    for(auto i:a){
        if(i.x<=mid){
            a1.push_back(i);
            merge(i.p,1);
        }
        else{
            a2.push_back(i);
        }
    }
    vector<query>q1,q2;
    int j=0;
    for(auto i:q){
        while(j<ch.size()&&ch[j].t<=i.t){
            if(ch[j].x<=mid)  merge(ch[j].p,ch[j].op);
            // printf("chenge %d %d\n",ch[j].p,ch[j].op);
            j++;//后j++故应到j-1
        }
        int res=ask(i.y)-ask(i.x-1);
        // printf("%d %d %d %d %d %d\n",i.x,i.y,i.k,res,l,r);
        if(i.k<=res)  q1.push_back(i);
        else  i.change(res),q2.push_back(i);
    }
    for(auto i:a){
        if(i.x<=mid){
            merge(i.p,-1);
        }
    }
    for(int i=0;i<j;i++){//下表从0开始
        if(ch[i].x<=mid)  merge(ch[i].p,-ch[i].op);
    }
    vector<change>ch1,ch2;
    for(auto i:ch){
        if(i.x<=mid)  ch1.push_back(i);
        else  ch2.push_back(i);
    }
    if(q1.size())  solve(l,mid,a1,q1,ch1);
    if(q2.size())  solve(mid+1,r,a2,q2,ch2);
}
int main(){
    scanf("%d%d",&n,&m);
    vector<num>a;
    vector<query>q;
    vector<change>ch;
    for(int i=1;i<=n;i++){
        scanf("%d",&s[i]);
        mxnum=max(mxnum,s[i]);
        a.push_back((num){s[i],i});
    }
    for(int i=1;i<=m;i++){
        int l,r,k;
        char c[2];
        scanf("%s",c);
        if(c[0]=='Q'){
            scanf("%d%d%d",&l,&r,&k);
            q.push_back({l,r,k,++cnt,i});
        }
        else{
            scanf("%d%d",&l,&r);
            mxnum=max(mxnum,r);//注:这里也要统计最大值
            ch.push_back({s[l],l,i,-1});
            ch.push_back({r,l,i,1});//两个操作
            s[l]=r;
        }
    }
    // for(auto i:ch){
    //     // printf("%d %d %d %d\n",i.p,i.t,i.x,i.op);
    // }
    solve(0,mxnum,a,q,ch);
    for(int i=1;i<=cnt;i++){//为cnt
        printf("%d\n",ans[i]);
    }
}

posted @ 2025-07-24 18:35  daydreamer_zcxnb  阅读(14)  评论(0)    收藏  举报