整体二分

引言

整体二分用于解决一些决策性单调的分治问题,其核心思想就是利用分治将同一个范围内的答案统一处理。

使用条件

  • 单个询问可以用二分解决(可二分性)。
  • 修改对答案贡献互相独立,修改之间互不影响
  • 允许离线。

例题

P3527 [POI 2011] MET-Meteors

单组询问可二分,操作相互独立,考虑整体二分。

考虑二分判定问题。有解的可能性随时间推移而增大,则时间作为一个单调域可以在其上整体二分。

代码:

#include <bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;

const int N = 3e5 + 5;
int n, m, k, o[N], p[N], l[N], r[N], a[N], P[N], ans[N];
vector<int> start, w[N], rem[N];

namespace BIT {
    int t[N];

    inline int lowbit(int x) {
        return x & -x;
    }

    inline void upd(int x, int k) {
        for( ; x <= m ; x += lowbit(x))
            t[x] += k;
        
        return ;
    }

    inline void add(int l, int r, int k) {
        if(l <= r) {
            upd(l, k);
            upd(r + 1, -k);
        }
        else {
            upd(1, k), upd(r + 1, -k);
            upd(l, k), upd(m + 1, -k);
        }
    }

    inline int query(int x) {
        int res = 0;

        for( ; x ; x -= lowbit(x))
            res += t[x];

        return res;
    }
}

using namespace BIT;

inline void solve(int L, int R, vector<int> q) {
    // cerr << q.size() << '\n';

    if(q.empty()) return ;

    if(L == R) {
        // cerr << "!";

        for(auto i : q) ans[i] = L;

        return ;
    }

    vector<int> lq, rq;

    int mid = (L + R) >> 1;

    for(int i = L ; i <= mid ; ++ i)
        add(l[i], r[i], a[i]);

    // cerr << "!";

    for(auto i : q) {
        // cerr << "!";

        __int128 sum = 0;

        for(auto j : w[i])
            sum += query(j);

        if(p[i] <= sum) lq.pb(i);
        else {
            rq.pb(i);
            p[i] -= sum;
        }

        // cerr << "sum:" << (long long)sum << '\n';
    }

    for(int i = L ; i <= mid ; ++ i)
        add(l[i], r[i], -a[i]);

    solve(L, mid, lq);
    solve(mid + 1, R, rq);
  
    return ;
}

signed main() {
    ios_base :: sync_with_stdio(NULL);
    cin.tie(nullptr);
    cout.tie(nullptr);

    cin >> n >> m;
    for(int i = 1 ; i <= m ; ++ i)
        cin >> o[i], w[o[i]].pb(i);
    for(int i = 1 ; i <= n ; ++ i)
        cin >> p[i], P[i] = p[i];
    cin >> k;
    for(int i = 1 ; i <= k ; ++ i)
        cin >> l[i] >> r[i] >> a[i];

    for(int i = 1 ; i <= n ; ++ i)
        start.pb(i);

    solve(1, k, start);

    for(int i = 1 ; i <= n ; ++ i)
        rem[ans[i]].pb(i);

    memset(t, 0, sizeof t);

    for(int i = 1 ; i <= k ; ++ i) {
        add(l[i], r[i], a[i]);

        for(auto j : rem[i]) {
            __int128 sum = 0;

            for(auto h : w[j])
                sum += query(h);

            if(sum < P[j]) ans[j] = -1;

            // cerr << j << ' ' << P[j] << ' ' << (long long)sum << ' ' << ans[j] << '\n';
        }
    }

    for(int i = 1 ; i <= n ; ++ i)
        if(ans[i] == -1) cout << "NIE\n";
        else cout << ans[i] << '\n';

    return 0;
}

P2617 Dynamic Rankings

简要题意:单点修区间 \(k\) th。

首先考虑弱化版:不带修。则单次询问可以二分,且询问相互独立,可以整体二分。

则带修版本可以看作是在其上增加了插入和删除操作,并到操作序列里即可。

代码:

#include <bits/stdc++.h>
// #define int long long
#define pb push_back
using namespace std;

const int N = 3e5 + 5;
const int INF = 1e9;
int n, Q, tot, a[N], l[N], r[N], k[N], x[N], y[N], ans[N], flag[N];
char op[N];
vector<int> start;

namespace BIT {
    int t[N];

    inline int lowbit(int x) {
        return x & -x;
    }

    inline void add(int x, int k) {
        for( ; x <= n ; x += lowbit(x))
            t[x] += k;

        return ;
    }

    inline int ask(int x) {
        int res = 0;

        for( ; x ; x -= lowbit(x))
            res += t[x];

        return res;
    }

    inline int query(int l, int r) {
        return ask(r) - ask(l - 1);
    }
}

using namespace BIT;

inline void solve(int L, int R, vector<int> q) {
    if(q.empty()) return ;

    // cerr << L << ' ' << R << '\n';

    // cerr << "!";

    if(L == R) {
        // cerr << "!";

        for(auto i : q) ans[i] = L;

        return ;
    }

    int mid = (L + R) >> 1;

    vector<int> ql, qr;

    for(auto i : q) {
        // cerr << "!";

        if(op[i] == 'C') {
            if(y[i] <= mid) {
                ql.pb(i);
                add(x[i], flag[i]);
            }
            else qr.pb(i);
        }
        else {
            int v = query(l[i], r[i]);

            if(v >= k[i]) ql.pb(i);
            else {
                k[i] -= v;
                qr.pb(i);
            }
        }
    }

    // cerr << "ql.size:" << ql.size() << '\n';
    // cerr << "qr.size:" << qr.size() << '\n';
    
    for(auto i : q)
        if(op[i] == 'C' && y[i] <= mid) add(x[i], -flag[i]);

    // for(auto i : ql) {
    //     cerr << op[i] << ' ';

    //     if(op[i] == 'C') {
    //         cerr << x[i] << ' ' << y[i] << ' ' << flag[i] << '\n';
    //     }
    //     else {
    //         cerr << l[i] << ' ' << r[i] << ' ' << k[i] << '\n';
    //     }
    // }

    solve(L, mid, ql);
    solve(mid + 1, R, qr);

    return ;
}

signed main() {
    // freopen("P2617_2.in", "r", stdin);
    // freopen("ans.out", "w", stdout);

    ios_base :: sync_with_stdio(NULL);
    cin.tie(nullptr);
    cout.tie(nullptr);

    cin >> n >> Q;
    for(int i = 1 ; i <= n ; ++ i) {
        ++ tot;
        cin >> a[i];
        op[tot] = 'C';
        x[tot] = i, y[tot] = a[i], flag[tot] = 1;
    }
    for(int i = 1 ; i <= Q ; ++ i) {
        cin >> op[++ tot];

        if(op[tot] == 'C') {
            cin >> x[tot] >> y[tot];
            flag[tot] = 1;
            op[++ tot] = 'C';
            x[tot] = x[tot - 1], y[tot] = a[x[tot - 1]], flag[tot] = -1;
            a[x[tot]] = y[tot - 1];
            swap(x[tot], x[tot - 1]), swap(y[tot], y[tot - 1]), swap(flag[tot], flag[tot - 1]);
        }
        else cin >> l[tot] >> r[tot] >> k[tot];
    }

    for(int i = 1 ; i <= tot ; ++ i)
        start.pb(i);

    solve(0, 1e9, start);

    for(int i = 1 ; i <= tot ; ++ i)
        if(op[i] == 'Q') cout << ans[i] << '\n';

    return 0;
}

P1527 [国家集训队] 矩阵乘法

静态矩阵第 \(k\) 小,将整体二分中的数据结构改用二维树状数组维护即可。

#include <bits/stdc++.h>
// #define int long long
#define pb emplace_back
using namespace std;

const int N = 505;
const int M = 6e4 + 5;
int n, Q, tot, x[N * N], y[N * N], k[N * N + M], xx[N * N + M], yy[N * N + M], xxx[N * N + M], yyy[N * N + M], op[N * N + M], ans[N * N + M];
vector<int> start;

namespace BIT {
    int t[N][N];

    inline int lowbit(int x) {
        return x & -x;
    }

    inline void add(int x, int y, int k) {
        for( ; x <= n ; x += lowbit(x))
            for(int z = y ; z <= n ; z += lowbit(z))
                t[x][z] += k;

        return ;
    }

    inline int ask(int x, int y) {
        int res = 0;

        for( ; x ; x -= lowbit(x))
            for(int z = y ; z ; z -= lowbit(z))
                res += t[x][z];

        return res;
    }

    inline int query(int x, int y, int xx, int yy) {
        return ask(xx, yy) - ask(x - 1, yy) - ask(xx, y - 1) + ask(x - 1, y - 1);
    }
}

using namespace BIT;

inline void solve(int L, int R, vector<int> q) {
    if(q.empty()) return ;

    if(L == R) {
        for(auto i : q) ans[i] = L;

        return ;
    }

    int mid = (L + R) >> 1;
    vector<int> ql, qr;

    for(auto i : q) {
        if(op[i] == 1) {
            if(k[i] <= mid) {
                ql.pb(i);
                add(x[i], y[i], 1);
            }
            else qr.pb(i);
        }
        else {
            int v = query(xx[i], yy[i], xxx[i], yyy[i]);

            if(v >= k[i]) ql.pb(i);
            else {
                // cerr << i << ' ' << k[i] << ' ' << v << '\n';
                qr.pb(i);
                k[i] -= v;
            }
        }
    }

    for(auto i : q)
        if(op[i] == 1 && k[i] <= mid) add(x[i], y[i], -1);

    // cerr << "pos:" << L << ' ' << R << '\n';
    // cerr << "ql.size:" << ql.size() << '\n';
    // cerr << "qr.size:" << qr.size() << '\n';

    // for(auto i : qr) cerr << i << ' ';
    // cerr << '\n';

    solve(L, mid, ql), solve(mid + 1, R, qr);

    return ;
}

signed main() {
    ios_base :: sync_with_stdio(NULL);
    cin.tie(nullptr);
    cout.tie(nullptr);

    cin >> n >> Q;
    for(int i = 1 ; i <= n ; ++ i)
        for(int j = 1 ; j <= n ; ++ j) {
            op[++ tot] = 1;
            x[tot] = i, y[tot] = j;
            cin >> k[tot];
        }
    for(int i = 1 ; i <= Q ; ++ i)
        op[++ tot] = 2, cin >> xx[tot] >> yy[tot] >> xxx[tot] >> yyy[tot] >> k[tot];
    
    for(int i = 1 ; i <= tot ; ++ i)
        start.pb(i);

    solve(1, 1e9, start);

    for(int i = 1 ; i <= tot ; ++ i)
        if(op[i] == 2) cout << ans[i] << '\n';
    
    // add(1, 1, 1), add(1, 2, 1), add(2, 1, 1), add(2, 2, 1);
    // cerr << query(1, 1, 2, 2) << '\n';
    // cerr << ask(2, 2) << '\n';

    return 0;
}

/*
2 1
2 1
3 4
1 1 2 2 3
*/
posted @ 2025-06-26 15:30  endswitch  阅读(32)  评论(0)    收藏  举报