整体二分

  • 前置知识 : 二分,一些数据结构如树状数组

  • 用途 : 用于解决多次可二分可离线的询问。

可以使用整体二分解决的题目应具有以下性质 :

  1. 询问的答案具有可二分性。
  2. 修改对判定答案的贡献互相独立,修改之间互不影响效果。
  3. 修改如果对判定答案有贡献,则贡献为一确定的与判定标准无关的值。
  4. 贡献满足交换律,结合律,具有可加性。
  5. 题目允许使用离线算法。

——许昊然《浅谈数据结构题几个非经典解法》

大体的流程是这样的 :

  1. 先将所有操作用数组记录下来,然后按时间顺序分治。
  2. 假设递归函数为work,那么一般会有4个参数 : \(l,r\)分别为答案左端点、右端点(就是二分答案时的left和right), \(ql,qr\)分别为操作的左端点,右端点(因为只有\(ql\sim qr\)的操作会对答案产生贡献)。
  3. 如果\(ql>qr\),直接return。如果\(l=r\),那么\(ql\sim qr\)之间查询操作的答案就是l,记录答案,return掉。
  4. 二分答案,记\(mid = \frac{l+r}{2}\)
  5. 遍历\(ql\sim qr\)的操作。
    1. 若为修改操作 : 如果当前操作影响的点会影响到mid,那么将其按照时间顺序记入一个临时数组\(lq\)中,用数据结构更改贡献,反之,那么将其按照时间顺序记入另一个临时数组\(rq\)中。
    2. 若为查询操作 : 如果当前操作的答案小于mid,将其按照时间顺序记入\(lq\)中,反之,减去贡献,按照时间顺序扔到\(rq\)中。
  6. 然后将\(lq\)所有更改操作的贡献还原,是一个一个复原,不是直接memset,不然复杂度是错误的。
  7. 然后将\(lq,rq\)依次并入最一开始的操作数组\(q\)中。
  8. 递归下一层,分别为\(work(l,mid,ql,ql+q1-1),work(mid+1,r,ql+q1,qr)\)

优势 : 一般常数较小如果您是大常数体质当我没说,比较好写,空间复杂度优秀。
劣势 : 必须离线。

常见的一般就是求k大值或者是可以通过题意转化成k大值的,还有求中位数的,套个cdq硬c树套树的?(雾)

但一般的题都是求k大值哎。

例题 :

  1. 【模板】可持久化线段树 2

主席树板子当然不能用主席树做啦

一看题 : 区间rank,典型的可二分,再一看,主席树板子竟然不强制在线? 整体二分,启动。

大概就是像上面的流程走就行了。

反正可以离线,那么就离散化,然后传入的值为由\(-INF,INF\)变成\(1,\text{不同值的数量}\),提升效率。

点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
#ifdef LOCAL
    FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
    // FILE *ErrFile=errfile("err.err");
#else
    FILE *Infile = stdin,*OutFile = stdout;
    //FILE *ErrFile = stderr;
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
const int N = 2e5 + 10;
int n,m,a[N];
class BIT{
private:
    int tree[N];
#define lowbit(x) (x&(-x))
public:
    int mx;
    inline void update(int pos,int val){
        for(int i = pos;i <= mx;i += lowbit(i)) tree[i] += val;
    }
    inline int query(int pos){
        int res = 0;
        for(int i = pos; i;i -= lowbit(i))
            res += tree[i];
        return res;
    }
}T;
struct node{int l,r,k,id,op;}q[N<<1],lq[N<<1],rq[N<<1];
int cnt,ans[N];
void work(int l,int r,int ql,int qr){
    if(ql > qr) return;
    if(l == r){
        for(int i = ql;i <= qr; ++i)
            if(q[i].op == 2) ans[q[i].id] = l;//记录答案
        return;
    }
    int mid = (l + r) >> 1,p1 = 0,p2 = 0;
    for(int i = ql;i <= qr; ++i){
        if(q[i].op == 1){
            if(q[i].k <= mid) T.update(q[i].l,1),lq[++p1] = q[i];//操作可以影响到当前二分的答案。记录贡献,存入左边
            else rq[++p2] = q[i];//影响不到答案,存入右边
        }
        else{
            int res = T.query(q[i].r) - T.query(q[i].l - 1);//查询
            if(res >= q[i].k) lq[++p1] = q[i];//答案要更小,记入左边。
            else{
                q[i].k -= res;
                rq[++p2] = q[i];//答案要更大,将以前的贡献减去,记入右边
            }
        }
    }
    for(int i = 1;i <= p1; ++i) if(lq[i].op == 1) T.update(lq[i].l,-1);//将贡献删去,注意不要用memset
    for(int i = 1;i <= p1; ++i) q[i + ql - 1] = lq[i];
    for(int i = 1;i <= p2; ++i) q[i + ql + p1 -1] = rq[i];//合并
    work(l,mid,ql,ql+p1-1);work(mid+1,r,ql+p1,qr);//递归进下一层
}
inline void solve(){
    cin>>n>>m;
    for(int i = 1;i <= n; ++i) cin>>a[i];
    vector<int> num;
    for(int i = 1;i <= n; ++i) num.emplace_back(a[i]);
    sort(num.begin(),num.end());
    for(int i = 1;i <= n; ++i) 
        a[i] = lower_bound(num.begin(),num.end(),a[i]) - num.begin() + 1;
    for(int i = 1;i <= n; ++i)
        q[++cnt] = {i,i,a[i],0,1};
    for(int i = 1,l,r,k;i <= m; ++i){
        cin>>l>>r>>k;
        q[++cnt] = {l,r,k,i,2};
    }
    T.mx = n;
    work(1,num.size(),1,cnt);
    for(int i = 1;i <= m; ++i) cout<<num[ans[i]-1]<<'\n';
}
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    cout.tie(nullptr)->sync_with_stdio(false);
    solve();    
}

还有好多经验可以加

  1. [国家集训队] 矩阵乘法

还是板板,变成二维树状数组即可。

点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
#ifdef LOCAL
    FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
    // FILE *ErrFile=errfile("err.err");
#else
    FILE *Infile = stdin,*OutFile = stdout;
    //FILE *ErrFile = stderr;
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
const int N = 510;
inline int lowbit(int x){return (x&(-x));}
class BIT{
private:
    int tree[N];
public:
    int mx;
    inline void update(int pos,int val){for(int i = pos;i <= mx;i += lowbit(i)) tree[i] += val;}
    inline int query(int pos){int res = 0;for(int i = pos; i;i -= lowbit(i)) res += tree[i];return res;}
}T[N];
int cnt,n,m,a[N][N],ans[N];
inline void update(int x,int y,int val){
    for(int i = x;i <= n;i += lowbit(i)) T[i].update(y,val);
}
inline int query(int x,int y){
    int res = 0;
    for(int i = x; i;i -= lowbit(i)) res += T[i].query(y);
    return res;
}
struct node{int op,l,r,k,x1,x2,y1,y2,id;}q[400010],lq[400010],rq[400010];
inline void work(int l,int r,int ql,int qr){
    if(ql > qr) return;
    if(l == r){
        // cerr<<l<<": "<<ql<<' '<<qr<<'\n';
        for(int i = ql;i <= qr; ++i){
            // cerr<<q[i].op<<' ';
            // if(q[i].op == 1) cerr<<q[i].l<<' '<<q[i].r<<' '<<q[i].k<<'\n';
            // else cerr<<q[i].x1<<' '<<q[i].y1<<' '<<q[i].x2<<' '<<q[i].y2<<'\n';
            if(q[i].op == 2) ans[q[i].id] = l;
        }
        // cerr<<"\n\n";
        return;
    }
    int mid = (l + r) >> 1,q1 = 0,q2 = 0;
    for(int i = ql;i <= qr; ++i){
        if(q[i].op == 1){
            if(q[i].k <= mid) update(q[i].l,q[i].r,1),lq[++q1] = q[i];
            else rq[++q2] = q[i];
        }
        else{
            int res = query(q[i].x2,q[i].y2) - query(q[i].x1-1,q[i].y2) - query(q[i].x2,q[i].y1-1) + query(q[i].x1-1,q[i].y1-1);
            if(res >= q[i].k) lq[++q1] = q[i];
            else{
                q[i].k -= res;
                rq[++q2] = q[i];
            }
        }
    }
    for(int i = 1;i <= q1; ++i) if(lq[i].op == 1) update(lq[i].l,lq[i].r,-1);
    for(int i = 1;i <= q1; ++i) q[i + ql - 1] = lq[i];
    for(int i = 1;i <= q2; ++i) q[i + ql + q1 - 1] = rq[i];
    work(l,mid,ql,ql+q1-1);
    work(mid+1,r,ql+q1,qr);
}
inline void solve(){
    cin>>n>>m;
    for(int i = 1;i <= n; ++i) T[i].mx = n;
    vector<int> num;
    for(int i = 1;i <= n; ++i) for(int j = 1;j <= n; ++j) cin>>a[i][j],num.emplace_back(a[i][j]);
    // for(int i = 1;i <= n; ++i){
    //     for(int j = 1;j <= n; ++j){
    //         cout<<query(i,j)<<' ';
    //     }
    //     cout<<'\n';
    // }
    // exit(0);
    sort(num.begin(),num.end());
    num.erase(unique(num.begin(),num.end()),num.end());
    for(int i = 1;i <= n; ++i) for(int j = 1;j <= n; ++j) a[i][j] = lower_bound(num.begin(),num.end(),a[i][j]) - num.begin() + 1;
    for(int i = 1;i <= n; ++i) for(int j = 1;j <= n; ++j) cnt++,q[cnt].op = 1,q[cnt].l = i,q[cnt].r = j,q[cnt].k = a[i][j];
    for(int i = 1;i <= m; ++i){
        cnt++;q[cnt].op = 2;q[cnt].id = i;
        cin>>q[cnt].x1>>q[cnt].y1>>q[cnt].x2>>q[cnt].y2>>q[cnt].k;
    }
    work(1,num.size(),1,cnt);
    for(int i = 1;i <= m; ++i) cout<<num[ans[i]-1]<<'\n';
}
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    cout.tie(nullptr)->sync_with_stdio(false);
    solve();    
}
  1. Dynamic Rankings

修改+区间rank,将修改分成两个操作 : 将原来的减去,将现在的加上。

然后套板板就好啦。

点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
#ifdef LOCAL
    FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
    // FILE *ErrFile=errfile("err.err");
#else
    FILE *Infile = stdin,*OutFile = stdout;
    //FILE *ErrFile = stderr;
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
const int N = 1e5 + 10;
struct node{int op,l,r,k,x,ad,pos,id;}q[N*3],lq[N*3],rq[N*3];
int cnt,n,a[N],m,ans[N];
class BIT{
private:int tree[N];inline int lowbit(int x){return (x&(-x));}
public: int mx;
    inline void update(int pos,int val){for(int i = pos;i <= mx;i += lowbit(i)) tree[i] += val;}
    inline int query(int pos){int res = 0;for(int i = pos; i;i -= lowbit(i)) res += tree[i];return res;}
}T;
void work(int l,int r,int ql,int qr){
    if(ql > qr) return;
    if(l == r){
        for(int i = ql;i <= qr; ++i){
            if(q[i].op == 2) ans[q[i].id] = l;
        }
        return;
    }
    int mid = (l + r) >> 1,q1 = 0,q2 = 0;
    for(int i = ql;i <= qr; ++i){
        if(q[i].op == 1){
            if(q[i].x <= mid) T.update(q[i].pos,q[i].ad),lq[++q1] = q[i];
            else rq[++q2] = q[i];
        }
        else{
            int res = T.query(q[i].r) - T.query(q[i].l - 1);
            if(res >= q[i].k) lq[++q1] = q[i];
            else{
                q[i].k -= res;
                rq[++q2] = q[i];
            }
        }
    }
    for(int i = 1;i <= q1; ++i) if(lq[i].op == 1) T.update(lq[i].pos,-lq[i].ad);
    for(int i = 1;i <= q1; ++i) q[ql + i - 1] = lq[i];
    for(int i = 1;i <= q2; ++i) q[ql + i + q1 - 1] = rq[i];
    work(l,mid,ql,ql+q1-1);work(mid+1,r,ql+q1,qr);
}
inline void solve(){
    cin>>n>>m;T.mx = n;
    vector<int> num;
    for(int i = 1;i <= n; ++i){
        cin>>a[i];cnt++;
        q[cnt].op = 1,q[cnt].x = a[i],q[cnt].ad = 1;q[cnt].pos = i;
        num.push_back(a[i]);
    }
    auto Get = [](char x) -> int{return (x == 'C'?1:2);};
    int p = 0;
    
    for(int i = 1;i <= m; ++i){
        char opt;cin>>opt;
        int op = Get(opt);
        if(op == 1){
            int x,y;cin>>x>>y;
            cnt++;q[cnt].op = 1,q[cnt].x = a[x],q[cnt].ad = -1;q[cnt].pos = x;
            a[x] = y;num.emplace_back(y);
            cnt++;q[cnt].op = 1,q[cnt].x = a[x],q[cnt].ad = 1;q[cnt].pos = x;
        }
        else{
            int l,r,k;cin>>l>>r>>k;p++;
            cnt++;q[cnt].op = 2,q[cnt].l = l,q[cnt].r = r,q[cnt].k = k;q[cnt].id = p;
        }
    }
    sort(num.begin(),num.end());
    num.erase(unique(num.begin(),num.end()),num.end());
    for(int i = 1;i <= cnt; ++i) 
        if(q[i].op == 1) 
            q[i].x = lower_bound(num.begin(),num.end(),q[i].x) - num.begin() + 1;
    work(1,num.size(),1,cnt);
    for(int i = 1;i <= p; ++i) cout<<num[ans[i] - 1]<<'\n';
}
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    cout.tie(nullptr)->sync_with_stdio(false);
    solve();
}

整体二分肆意薄纱树套树反正我线段树套pbds的平衡树只拿了85pts

  1. [THUPC2017] 天天爱射击

还是板板,把第一道题的代码贺下来改改就可过。

点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
#ifdef LOCAL
    FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
    // FILE *ErrFile=errfile("err.err");
#else
    FILE *Infile = stdin,*OutFile = stdout;
    //FILE *ErrFile = stderr;
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
const int N = 2e5 + 10;
int n,m,a[N];
class BIT{
private:
    int tree[N];
#define lowbit(x) (x&(-x))
public:
    int mx;
    inline void update(int pos,int val){
        for(int i = pos;i <= mx;i += lowbit(i)) tree[i] += val;
    }
    inline int query(int pos){
        int res = 0;
        for(int i = pos; i;i -= lowbit(i))
            res += tree[i];
        return res;
    }
}T;
struct node{int l,r,k,id,op;}q[N<<1],lq[N<<1],rq[N<<1];
int cnt,ans[N];
void work(int l,int r,int ql,int qr){
    if(ql > qr) return;
    if(l == r){
        // cerr<<l<<'\n';
        for(int i = ql;i <= qr; ++i)
            if(q[i].op == 2) ans[l]++;
        return;
    }
    int mid = (l + r) >> 1,p1 = 0,p2 = 0;
    for(int i = ql;i <= qr; ++i){
        if(q[i].op == 1){
            if(q[i].k <= mid) T.update(q[i].l,1),lq[++p1] = q[i];
            else rq[++p2] = q[i];
        }
        else{
            int res = T.query(q[i].r) - T.query(q[i].l - 1);
            if(res >= q[i].k) lq[++p1] = q[i];
            else{
                q[i].k -= res;
                rq[++p2] = q[i];
            }
        }
    }
    for(int i = 1;i <= p1; ++i) if(lq[i].op == 1) T.update(lq[i].l,-1);
    for(int i = 1;i <= p1; ++i) q[i + ql - 1] = lq[i];
    for(int i = 1;i <= p2; ++i) q[i + ql + p1 -1] = rq[i];
    work(l,mid,ql,ql+p1-1);
    work(mid+1,r,ql+p1,qr);
}
int l[N],r[N],t[N];
inline void solve(){
    cin>>n>>m;int mx = 0;
    for(int i = 1;i <= n; ++i) cin>>l[i]>>r[i]>>t[i],mx = max(mx,r[i]);
    for(int i = 1,x;i <= m; ++i){
        cin>>x;mx = max(x,mx);
        q[++cnt] = {x,0,i,0,1};
    }
    T.mx = mx;
    for(int i = 1;i <= n; ++i) q[++cnt] = {l[i],r[i],t[i],i,2};
    work(1,m+1,1,cnt);
    for(int i = 1;i <= m; ++i) cout<<ans[i]<<'\n';
}
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    cout.tie(nullptr)->sync_with_stdio(false);
    solve();    
}
  1. [POI2011] MET-Meteors

经典题。

就是树状数组区间修改,单点查询,对于每个星球的查询暴力就行了。

注意树状数组会爆ll,开ull即可。

点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
#ifdef LOCAL
    FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
    // FILE *ErrFile=errfile("err.err");
#else
    FILE *Infile = stdin,*OutFile = stdout;
    //FILE *ErrFile = stderr;
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
const int N = 3e5 + 10;
#define int ull
int n,m,cnt,a[N],p,ans[N];
vector<int> have[N];
struct node{int l,r,k,id,op;}q[N*3],lq[N*3],rq[N*3];
class BIT{
private:ll tree[N];inline int lowbit(int x){return (x&(-x));}
public:int mx;
    inline void update(int pos,int val){for(int i = pos;i <= mx;i += lowbit(i)) tree[i] += val;}
    inline ll query(int pos){ll res = 0;for(int i = pos; i;i -= lowbit(i)) res += tree[i];return res;}
}T;
void work(int l,int r,int ql,int qr){
    if(ql > qr) return;
    if(l == r){
        for(int i = ql;i <= qr; ++i) if(q[i].op == 2) ans[q[i].id] = l;
        return;
    }
    int mid = (l + r) >> 1,q1 = 0,q2 = 0;
    for(int i = ql;i <= qr; ++i){
        if(q[i].op == 1)
            if(q[i].id <= mid) T.update(q[i].l,q[i].k),T.update(q[i].r+1,-q[i].k),lq[++q1] = q[i];
            else rq[++q2] = q[i];
        else{
            ll res = 0;
            for(auto x : have[q[i].id]) res += T.query(x);
            if(res >= q[i].k) lq[++q1] = q[i];
            else q[i].k -= res,rq[++q2] = q[i];
        }
    }
    for(int i = 1;i <= q1; ++i) if(lq[i].op == 1) T.update(lq[i].l,-lq[i].k),T.update(lq[i].r+1,lq[i].k);
    for(int i = 1;i <= q1; ++i) q[i + ql - 1] = lq[i];
    for(int i = 1;i <= q2; ++i) q[i + ql + q1 - 1] = rq[i];
    work(l,mid,ql,ql+q1-1);work(mid+1,r,ql+q1,qr);
}
inline void solve(){
    cin>>n>>m;T.mx = m;
    for(int i = 1,x;i <= m; ++i) cin>>x,have[x].emplace_back(i);
    for(int i = 1;i <= n; ++i) cin>>a[i];
    cin>>p;
    for(int i = 1,l,r,k;i <= p; ++i){
        cin>>l>>r>>k;
        if(l <= r) q[++cnt] = {l,r,k,i,1};
        else q[++cnt] = {l,m,k,i,1},q[++cnt] = {1,r,k,i,1};
    }
    for(int i = 1;i <= n; ++i) q[++cnt] = {i,i,a[i],i,2};
    work(1,p+1,1,cnt);
    for(int i = 1;i <= n; ++i) if(ans[i] <= p) cout<<ans[i]<<'\n';else cout<<"NIE\n";
}
signed main(){
    cin.tie(nullptr)->sync_with_stdio(false);
    cout.tie(nullptr)->sync_with_stdio(false);
    solve();
}
posted @ 2024-08-24 20:58  CuFeO4  阅读(5)  评论(0编辑  收藏  举报