整体二分总结
Posted hlw1
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了整体二分总结相关的知识,希望对你有一定的参考价值。
简介
整体二分利用的是分治的思想,可以解决一些区间 kth 问题,是一种离线算法,可以对比主席树算法。
限制
题目需要满足以下条件:
- 询问的答案具有可二分性
- 修改对判定答案的贡献互相独立 ,修改之间互不影响效果
- 修改如果对判定答案有贡献,则贡献为一确定的与判定标准无关的值
- 贡献满足交换律,结合律,具有可加性
- 题目允许使用离线算法
——许昊然《浅谈数据结构题几个非经典解法》
算法
在 solve(l, r, L, R) 中,以 [l, r] 为答案的值域, [L, R] 为当前询问的区间,执行以下流程。
- 如果 l == r ,则记录区间内所有询问的答案。
- 按照时间顺序进行所有的操作,利用一些数据结构统计答案。
- 对于询问,我们根据查询的答案与询问需要的答案之间的关系,将操作序列分成两份,分别递归处理。
「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;
}
以上是关于整体二分总结的主要内容,如果未能解决你的问题,请参考以下文章