ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

整体二分

2022-07-12 08:34:27  阅读:155  来源: 互联网

标签:二分 int 值域 询问 整体 修改 ql


概念

当有很多询问,每个询问都可以通过二分解决,但是对每个询问都二分一次的时间复杂度不能接受,不妨将所有询问同时二分,是为整体二分。

要求:

  1. 允许离线。

  2. 修改之间互相独立,且具有可加性。

  3. 答案可以二分。

例题

全局第 k 小

在一个序列中多次查找第 \(k\) 小的数。

设当前询问的答案在值域 \([l, r]\) 内。令 \(m = \lfloor \frac{l + r}{2} \rfloor\),考虑对于当前值域内的每一个询问判断其答案属于 \([l, m]\) 还是 \((m, r]\)。显然地,可以使用值域树状数组维护全局小于等于 \(m\) 的值的个数,若其大于等于 \(k\) 说明在左半侧,否则在右半侧。

显然每个询问至多经过 \(\log\) 次划分得到答案,共有 \(n\) 个询问,单次划分单个询问的复杂度是 \(O(\log n)\),所以总时间复杂度是 \(O(n \log^2 n)\)

代码大概如下所示:

void solve(int l, int r, int ql, int qr)
{
	if (ql > qr) return;
	if (l == r)
	{
		for (int i = ql; i <= qr; i++) ans[q[i].idx] = l;
		return;
	}
	int cnt1 = 0, cnt2 = 0;
	int mid = (l + r) >> 1;
	for (int i = ql; i <= qr; i++)
	{
		if (query(mid) >= q[i].k) q1[++cnt1] = q[i];
		else q2[++cnt2] = q[i];
	}
	for (int i = 1; i <= cnt1; i++) q[ql + i - 1] = q1[i];
	for (int i = 1; i <= cnt2; i++) q[ql + cnt1 + i - 1] = q2[i];
	solve(l, mid, ql, ql + cnt1 - 1);
	solve(mid + 1, r, ql + cnt1, qr);
}

区间第 k 小

多次查询指定区间中第 k 小的值。

考虑将初始数组看作是 \(n\) 次分别在下标 \(1\) 到 \(n\) 上的修改,在整体二分时一同维护。每次划分时处理值在 \([l, m]\) 内的修改,即在其修改的位置加 \(1\)。用树状数组维护区间和,如果查询的区间 \([L, R]\) 的和大于等于 \(k\),说明区间第 \(k\) 小的值在 \([l, m]\) 内,划分即可。反之,记该区间再 \([l, m]\) 内的值个数为 \(x\),我们则需要查询该区间内值域在 \((m, r]\) 内的第 \(k - x\) 小值。

注意此时要先将 \(n\) 次修改压入操作数组,原因是每次划分后操作按初始顺序排列,于是需要先处理修改再询问。

容易发现划分前只关心值域在 \([l, m]\) 内的修改,所以直接将修改也随值域一同划分即可。

时间复杂度 \(O(n \log^2 n)\)

动态区间第 k 小

P2617 Dynamic Rankings

考虑把修改操作像上面一样压入数组,于是做完了。

#include <cstdio>
using namespace std;

const int maxn = 3e5 + 5;
const int maxm = 3e5 + 5;

struct item
{
    int l, r, k, idx, opt;

    item() : l(), r(), k(), idx(), opt() {}
    item(int _l, int _r, int _k, int _idx, int _opt) : l(_l), r(_r), k(_k), idx(_idx), opt(_opt) {}
} q[maxm], q1[maxm], q2[maxm];

int n, m;
int a[maxn], c[maxn];
int ans[maxm];

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

void update(int p, int w) { for (int i = p; i <= n; i += lowbit(i)) c[i] += w; }

int query(int p)
{
    int sum = 0;
    for (int i = p; i; i -= lowbit(i)) sum += c[i];
    return sum;
}

void solve(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].opt == 2) ans[q[i].idx] = l;
        return;
    }
    int cnt1 = 0, cnt2 = 0;
    int mid = (l + r) >> 1;
    for (int i = ql; i <= qr; i++)
    {
        if (q[i].opt == 1)
        {
            if (q[i].l <= mid)
            {
                q1[++cnt1] = q[i];
                update(q[i].idx, q[i].r);
            }
            else q2[++cnt2] = q[i];
        }
        else
        {
            int t = query(q[i].r) - query(q[i].l - 1);
            if (q[i].k <= t) q1[++cnt1] = q[i];
            else
            {
                q[i].k -= t;
                q2[++cnt2] = q[i];
            }
        }
    }
    for (int i = 1; i <= cnt1; i++)
        if (q1[i].opt == 1) update(q1[i].idx, -q1[i].r);
    for (int i = 1; i <= cnt1; i++) q[ql + i - 1] = q1[i];
    for (int i = 1; i <= cnt2; i++) q[ql + cnt1 + i - 1] = q2[i];
    solve(l, mid, ql, ql + cnt1 - 1);
    solve(mid + 1, r, ql + cnt1, qr);
}

int main()
{
    int cnt = 0, cur = 0;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
        q[++cnt] = item(a[i], 1, 0, i, 1);
    }
    for (int i = 1; i <= m; i++)
    {
        char opt;
        scanf(" %c ", &opt);
        if (opt == 'Q')
        {
            int l, r, k;
            scanf("%d%d%d", &l, &r, &k);
            q[++cnt] = item(l, r, k, ++cur, 2);
        }
        else
        {
            int x, y;
            scanf("%d%d", &x, &y);
            q[++cnt] = item(a[x], -1, 0, x, 1);
            q[++cnt] = item(y, 1, 0, x, 1);
            a[x] = y;
        }
    }
    solve(0, 1e9, 1, cnt);
    for (int i = 1; i <= cur; i++) printf("%d\n", ans[i]);
    return 0;
}

标签:二分,int,值域,询问,整体,修改,ql
来源: https://www.cnblogs.com/lingspace/p/zheng-ti-er-fen.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有