码迷,mamicode.com
首页 > 其他好文 > 详细

整体二分专题

时间:2019-03-19 21:40:12      阅读:157      评论:0      收藏:0      [点我收藏+]

标签:algo   处理   说明   for   poj   mat   zoj   dynamic   printf   

整体二分专题

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\n", an[i]);
    return 0;
}

B

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

C

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

整体二分专题

标签:algo   处理   说明   for   poj   mat   zoj   dynamic   printf   

原文地址:https://www.cnblogs.com/ikihsiguoyr/p/10561307.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!