整体二分专题

Posted ikihsiguoyr

tags:

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

整体二分专题

A - K-th Number [POJ - 2104 ]

(1 <= n <= 100 000, 1 <= m <= 5 000), 给出数组(a[1..n](leq1e9))

然后有(m)个询问, 每次询问(Q(l,r,k))(a[l..r])之间从小到大第(k)个的数

B - Dynamic Rankings [ZOJ-2112]

(1 <= N <= 50,000 ~~~~ 1 <= M <= 10,000), 给出数组(a[1..n](leq1e9))

然后有(m)个操作:

  • (C~i~t)(a[i])改为(t)

  • (Q~i~j~k)(a[l..r])之间从小到大第?k个的数

C - K大数查询 [HYSBZ - 3110]

(N)个位置,(M)个操作。操作有两种,每次操作如果是(1~a~b~c)的形式表示在第a个位置到第(b)个位置,每个位置加入一个数(c)
如果是(2~a~b~c)形式,表示询问从第(a)个位置到第(b)个位置,第(C)大的数是多少。

【样例说明】

第一个操作 后位置 1 的数只有 1 , 位置 2 的数也只有 1 。 第二个操作 后位置 1的数有 1 、 2 ,位置 2 的数也有 1 、 2 。 第三次询问 位置 1 到位置 1 第 2 大的数 是1 。 第四次询问 位置 1 到位置 1 第 1 大的数是 2 。 第五次询问 位置 1 到位置 2 第 3大的数是 1 。?

(N,M<=50000,N,M<=50000) (a<=b<=N)

1操作中(abs(c)<=N) 2操作中(c<=Maxlongint)

思路

A

将数组数值和询问一起放入操作序列(a[])

(solve(x, y, l, r))代表操作编号在([x,y])间的询问的答案在([l,r])之间, 并且当前([x,y])间的询问只能由([x,y])之间的数值贡献?

(solve())中:

  • (mid = (l + r) >> 1), 假设询问的答案为(mid),那么要检查(leq mid)的个数符不符合每个询问要求的个数
  • 如果这个操作是数组中的数, 则
    • (leq mid), 则放入(a_l[])中,这部分对之后的询问有贡献,将位置加入树状数组
    • 否则放入(a_r[])
  • 否则就是一个询问, 调用树状数组,求(leq mid)的个数
    • 如果(geq)需要的个数,那么实际答案比(mid)小(等于), 放入(a_l[])
    • 否则实际答案比(mid)大,减去现在贡献, 放入(a_r[])

(和放回a_l[]和a_r[]放回a[])中, (区间区间solve(l区间,l,mid), solve(r区间, mid+1,r))

如果(l==r)则区间内的询问答案都确定了

到边界就return

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>

using namespace std;
typedef long long LL;
const int MAXN = 1e5 + 10;
const int INF  = 1e9 + 10;

inline int in()
{
    int x = 0, flag = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') flag = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
    return x * flag;
}

int n, m, tot;

struct Fenwick
{
    int val[MAXN];
    void upd(int x, int v)
        {
            for (int i = x; i <= 100000; i += (i & (-i))) val[i] += v;
        }
    int qry(int x)
        {
            int ret = 0;
            for (int i = x; i > 0; i &= (i - 1)) ret += val[i];
            return ret;
        }
} T;

struct Node
{
    int v, l, r, p, typ, id;
} a[MAXN << 1], al[MAXN << 1], ar[MAXN << 1];
int an[MAXN];
void solve(int x, int y, int l, int r)
{
    if (l > r || x >= y) return ;
    if (l == r)
    {
        for (int i = x; i <= y; i ++)
            if (a[i].typ == 1) an[a[i].id] = l;
        return;
    }
    int mid = (l + r) >> 1;
    int cntl = 0, cntr = 0;
    for (int i = x; i <= y; i ++)
    {
        if (a[i].typ == 0)
        {
            if (a[i].v <= mid) al[++cntl] = a[i], T.upd(a[i].id, 1);
            else ar[++cntr] = a[i];
        }
        else
        {
            int val = T.qry(a[i].r) - T.qry(a[i].l - 1);
            if (a[i].p <= val) al[++cntl] = a[i];
            else a[i].p -= val, ar[++cntr] = a[i];
        }
    }
    for (int i = x; i <= y; i ++)
        if (a[i].typ == 0 && a[i].v <= mid) T.upd(a[i].id, -1);
    for (int i = 1; i <= cntl; i ++) a[x + i - 1] = al[i];
    for (int i = 1; i <= cntr; i ++) a[x + cntl + i - 1] = ar[i];
    solve(x, x + cntl - 1, l, mid);
    solve(y - cntr + 1, y, mid + 1, r);
}

int main()
{
    n = in(); m = in();
    for (int i = 1; i <= n; i ++)
    {
        a[++ tot].v = in();
        a[tot].typ = 0;
        a[tot].id = i;
    }
    for (int i = 1; i <= m; i ++)
    {
        a[++ tot] = (Node) {0, in(), in(), in(), 1, i };
    }
    solve(1, tot, -INF, INF);
    for (int i = 1; i <= m; i ++) printf("%d
", an[i]);
    return 0;
}

B

小改改(思考如何转化change)

C

小改改(思考如何处理区间而非前缀)

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

二分图匹配入门专题1I - Hiding Gold light oj 1152二分图匹配-------------------我是终于不那么水的水题分割线------------------(代码片

BZOJ 1901 & 整体二分

整体二分

算法专题(01)二分查找(01) 简单LeetCode 704

整体二分

算法专题(01)二分查找(03) 简单LeetCode 278