整体二分总结

Posted hlw1

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了整体二分总结相关的知识,希望对你有一定的参考价值。

简介

整体二分利用的是分治的思想,可以解决一些区间 kth 问题,是一种离线算法,可以对比主席树算法。

限制

题目需要满足以下条件:

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

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

算法

在 solve(l, r, L, R) 中,以 [l, r] 为答案的值域, [L, R] 为当前询问的区间,执行以下流程。

  1. 如果 l == r ,则记录区间内所有询问的答案。
  2. 按照时间顺序进行所有的操作,利用一些数据结构统计答案。
  3. 对于询问,我们根据查询的答案与询问需要的答案之间的关系,将操作序列分成两份,分别递归处理。

「Luogu P3527」[POI2011]Meteors

给定一个环,每个节点有一个所属国家,(k) 次事件,每次对 ([l, r]) 区间上的每个点点权加上一个值,求每个国家最早多少次操作之后所有点的点权和能达到一个值。

二分的值域是 1 到 k + 1 ,每次都把 1 到 mid 的陨石雨的贡献加进来,用树状数组统计,然后计算对当前询问区间的关系就好了。

卡卡常还是 T 了一个点,吸氧可以过,应该是用了 vector 的原因

#include <bits/stdc++.h>

#define N 300003
#define rg register
#define ll long long
#define lowbit(i) i&-i

using namespace std;

int gi() {
    int x = 0, f = 1; char c = getchar();
    for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
    for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
    return x * f;
}

int n, m, k;
int ans[N];
ll c[N << 1];

vector <int> vec[N];
vector <int> :: iterator it;

struct qaq {
    ll p; int id;
} q[N], q1[N], q2[N];

struct rain {
    int l, r; ll a;
} g[N];

inline void add(int i, ll k) {
    while (i <= 2 * m) {
        c[i] += k;
        i += lowbit(i);
    }
}

inline ll query(int i) {
    rg ll ret = 0;
    while (i) {
        ret += c[i];
        i -= lowbit(i);
    }
    return ret;
}

inline void solve(int l, int r, int L, int R) {
    if (l > r || L > R) return;
    if (l == r) {
        for (rg int i = L; i <= R; ++i) ans[q[i].id] = l;
        return;
    }
    rg int mid = l + r >> 1, cnt1 = 0, cnt2 = 0;
    for (rg int i = l; i <= mid; ++i) add(g[i].l, g[i].a), add(g[i].r + 1, -g[i].a);
    for (rg int i = L; i <= R; ++i) {
        rg ll tmp = 0;
        for (it = vec[q[i].id].begin(); it != vec[q[i].id].end(); ++it) {
            tmp += query(*it) + query(*it + m);
            if (tmp >= q[i].p) break;
        }
        if (tmp >= q[i].p) q1[++cnt1] = q[i];
        else q[i].p -= tmp, q2[++cnt2] = q[i];
    }
    for (rg int i = l; i <= mid; ++i) add(g[i].l, -g[i].a), add(g[i].r + 1, g[i].a);
    for (rg int i = 1; i <= cnt1; ++i) q[L + i - 1] = q1[i];
    for (rg int i = 1; i <= cnt2; ++i) q[L + cnt1 + i - 1] = q2[i];
    solve(l, mid, L, L + cnt1 - 1), solve(mid + 1, r, L + cnt1, R);
}

int main() {
    n = gi(), m = gi();
    for (rg int i = 1; i <= m; ++i) vec[gi()].push_back(i);
    for (rg int i = 1; i <= n; ++i) q[i] = (qaq){gi(), i};
    k = gi();
    for (rg int i = 1; i <= k; ++i) {
        g[i] = (rain){gi(), gi(), gi()};
        if (g[i].l > g[i].r) g[i].r += m;
    }
    solve(1, k + 1, 1, n);
    for (rg int i = 1; i <= n; ++i)
        if (ans[i] > k) puts("NIE");
        else printf("%d
", ans[i]);
    return 0;
}

「Luogu P1527」矩阵乘法

给你一个 (N*N) 的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第 (K) 小数。

用一个二维的树状数组即可,但这里直接二分值域可能会 T 。

蒟蒻第一次打二维树状数组,调了半天才发现原来是树状数组的锅,但还不知道为什么不能那么写......

#include <bits/stdc++.h>

#define N 503
#define M 60003
#define lowbit(i) (i&-i)
#define DEBUG puts("ok")

using namespace std;

int gi() {
    int x = 0, f = 1; char c = getchar();
    for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
    for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
    return x * f;
}

struct qry {
    int x1, y1, x2, y2, k, id;
} q[M], q1[M], q2[M];

struct Matrix {
    int x, y, val;
    friend bool operator < (Matrix a, Matrix b) {
        return a.val < b.val;
    }
} g[N * N];

int n, m, len;
int c[N][N], ans[M];
/*
void add(int x, int y, int k) {
    for ( ; x <= n; x += lowbit(x))
        for ( ; y <= n; y += lowbit(y))
            c[x][y] += k;
}

int query(int x, int y) {
    int ret = 0;
    for ( ; x; x -= lowbit(x))
        for ( ; y; y -= lowbit(y))
            ret += c[x][y];
    return ret;
}
*/
void add(int x, int y, int k) {
    for (int i = x; i <= n; i += lowbit(i))
        for (int j = y; j <= n; j += lowbit(j))
            c[i][j] += k;
}

int query(int x, int y) {
    int ret = 0;
    for (int i = x; i > 0; i -= lowbit(i))
        for (int j = y; j > 0; j -= lowbit(j))
            ret += c[i][j];
    return ret;
}

int get(int x1, int y1, int x2, int y2) {
    --x1, --y1;
    return query(x2, y2) + query(x1, y1) - query(x1, y2) - query(x2, y1);
}

void solve(int l, int r, int L, int R) {
    if (l > r || L > R) return;
    if (l == r) {
        for (int i = L; i <= R; ++i) ans[q[i].id] = g[l].val;
        return;
    }
    int mid = l + r >> 1, cnt1 = 0, cnt2 = 0;
    for (int i = l; i <= mid; ++i) add(g[i].x, g[i].y, 1);
    for (int i = L; i <= R; ++i) {
        int tmp = get(q[i].x1, q[i].y1, q[i].x2, q[i].y2);
        if (tmp >= q[i].k) q1[++cnt1] = q[i];
        else q[i].k -= tmp, q2[++cnt2] = q[i];
    }
    for (int i = l; i <= mid; ++i) add(g[i].x, g[i].y, -1);
    for (int i = 1; i <= cnt1; ++i) q[L + i - 1] = q1[i];
    for (int i = 1; i <= cnt2; ++i) q[L + cnt1 + i - 1] = q2[i];
    solve(l, mid, L, L + cnt1 - 1), solve(mid + 1, r, L + cnt1, R);
}

int main() {
    n = gi(), m = gi();
    for (int i = 1; i <= n; ++i)
    for (int j = 1; j <= n; ++j)
        g[++len] = (Matrix){i, j, gi()};
    sort(g + 1, g + 1 + len);
    for (int i = 1; i <= m; ++i) q[i] = (qry){gi(), gi(), gi(), gi(), gi(), i};
    solve(1, len, 1, m);
    for (int i = 1; i <= m; ++i) printf("%d
", ans[i]);
    return 0;
}

以上是关于整体二分总结的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ 1901 & 整体二分

CDQ分治与整体二分小结

整体二分

整体二分

整体二分QAQ

POJ2104 K-th Number(整体二分)