整体二分 (无带修)

整体二分

不带修的整体二分是离线的,除了占用空间小,用处不是很多。但是…你也想不到出题人会不会卡你空间,所以,还是看看吧…

前置知识

  • 二分答案
  • 树状数组
  • 分治

首先,能用整体二分的要满足以下条件:

  1. 对单个子问题可以用二分答案解决问题
  2. 题目是离线的,没有强制在线
  3. 子问题对答案的贡献可叠加

接下来进入正题(不会二分答案的可以先去学学再来看)
首先先看一道题(洛谷p3834)
在这里插入图片描述
考虑对某一个询问进行处理,我们想到的是二分答案,不仅如此,在这道题中,所有的询问都是离线的,那我们可以试试整体二分。
(下面的图片是 oi wiki给出的思路)
在这里插入图片描述

所谓的整体二分,就是对所有的问题进行二分答案,比如在这个题,我们第一次二分的值是15770(假设),那我们就把所有小于等于15770的位置上(实际上也就是值在二分区间[l, mid]之间的位置,因为分治写法的原因,能够保证当前的询问都大于l)都加上1,然后在对单一的询问时,我们就可以根据询问的区间求个前缀和,然后就能知道这个区间内小于等于15770的数有多少个(设为x),当x大于等于该询问的K时,说明该询问的答案小于当前二分的值,那么该询问就应该放入下一次整体二分答案的左区间,否则的话,就放入右 区间,并把当前询问改成第k - x大(因为满足了贡献可加性),对前缀和的求法,我们采用树状数组。
(如果听不太懂的话,看看接下来模拟,应该就能懂了)
//
为了方便,把样例看成是离散化后的数据,也就是
5 5
3 1 2 4 5
询问取前三个
2 2 1
3 4 1
4 5 1
//

在这里插入图片描述

最后贴上代码
// 这玩意复杂度也是nlogn,跑的和主席树差不多

#include <bits/stdc++.h>
#define endl '\n'
#define pb push_back


using namespace std;
using ll = long long;
using pii = pair<int, int>;


const int maxn = 2e5 + 10;
const int mod = 998244353;
const int inf = 0x3f3f3f3f;

struct Query
{
    int type, l, r, k, id;
    Query(int b, int c, int d, int e)
    {
        type = 1, l = b, r =  c, k = d, id = e;
    }
    Query(int a, int b)
    {
        type = 0, l = r = a, k = b, id = 0;
    }
    Query(){}
}q[maxn << 1], ql[maxn << 1], qr[maxn << 1];

int n, m;
int tree[maxn], ans[maxn];

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

int sum(int num)
{
    int ans = 0;
    while (num)
    {
        ans += tree[num];
        num -= lowbit(num);
    }
    return ans;
}

void add(int num, int val)
{
    while (num <= n)
    {
        tree[num] += val;
        num += lowbit(num);
    }
}

void solve(int l, int r, int lef, int rig)
{
    if (lef > rig) return;
    int i;
    if (l == r)
    {
        for (i = lef; i <= rig; i++)
            if (q[i].type) ans[q[i].id] = l;
        return ;
    }
    int mid = l + r >> 1, cnt1 = 0, cnt2 = 0;
    for (i = lef; i <= rig; i++)
    {
        if (!q[i].type)
        {
            if (q[i].k <= mid)
            {
                add(q[i].l, 1);
                ql[++cnt1] = q[i];
            }
            else qr[++cnt2] = q[i];
            continue;
        }
        int x = sum(q[i].r) - sum(q[i].l - 1);
        if (x >= q[i].k)
            ql[++cnt1] = q[i];
        else
        {
            q[i].k -= x;
            qr[++cnt2] = q[i];
        }
    }
    for (i = 1; i <= cnt1; i++)
        if (!ql[i].type) add(ql[i].l, -1);
    for (i = 1; i <= cnt1; i++)
        q[i] = ql[i];
    for (i = 1; i <= cnt2; i++)
        q[i + cnt1] = qr[i];
    solve(l, mid, 1, cnt1);
    solve(mid + 1, r, cnt1 + 1, cnt1 + cnt2);
}

pii a[maxn];
int val[maxn];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);

    cin >> n >> m;
    int i, j;
    for (i = 1; i <= n; i++)
        cin >> a[i].first, a[i].second = i;
    sort(a + 1, a + 1 + n);

    int cnt = 0;
    a[0].first = 1e9;
    for (i = 1; i <= n; i++)
    {
        if (a[i].first != a[i - 1].first)
            q[i] = Query(a[i].second, ++cnt), val[cnt] = a[i].first;
        else
            q[i] = Query(a[i].second, cnt);
    }

    int l, r, k;
    for (i = 1; i <= m; i++)
    {
        cin >> l >> r >> k;
        q[i + n] = Query(l, r, k, i);
    }
    solve(1, cnt, 1, n + m);
    for (i = 1; i <= m; i++)
        cout << val[ans[i]] << endl;
}

再结合代码看看,估计就可以了…

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值