整体二分专题

整体二分专题

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

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

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

时间: 2024-10-08 18:39:42

整体二分专题的相关文章

CDQ分治与整体二分小结

前言 这是一波强行总结. 下面是一波瞎比比. 这几天做了几道CDQ/整体二分,感觉自己做题速度好慢啊. 很多很显然的东西都看不出来 分治分不出来 打不出来 调不对 上午下午晚上的效率完全不一样啊. 完蛋.jpg 绝望.jpg. 关于CDQ分治 CDQ分治,求的是三维偏序问题都知道的. 求法呢,就是在分治外面先把一维变成有序 然后分治下去,左边(l,mid)关于右边(mid+1,r)就不存在某一维的逆序了,所以只有两维偏序了. 这个时候来一波"树状数组求逆序对"的操作搞一下二维偏序 就可

BZOJ 3110: [Zjoi2013]K大数查询 [整体二分]

有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. N,M<=50000,N,M<=50000a<=b<=N1操作中abs(c)<=N2操作中c<=Maxlongint 之前用树套树抄过一次...然而我并不适合写那玩意儿... 加上时间序的整体二分 普通的整体二分先处理了所有$[l,mid]$的影响因子在计算询问的答案来分组

bzoj1146整体二分+树链剖分+树状数组

其实也没啥好说的 用树状数组可以O(logn)的查询 套一层整体二分就可以做到O(nlngn) 最后用树链剖分让序列上树 1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 using namespace std; 6 inline int read() 7 { 8 int x=0,f=1,ch=getchar(); 9 while(ch<

(困难) CF 484E Sign on Fence,整体二分+线段树

Bizon the Champion has recently finished painting his wood fence. The fence consists of a sequence of n panels of 1 meter width and of arbitrary height. The i-th panel's height is hi meters. The adjacent planks follow without a gap between them. Afte

CDQ分治与整体二分总结

Cdq分治和整体二分是两个很奇妙的东西.他们都是通过离线的思想来进行优化,从而更快的求出解. 整体二分通俗的讲就是二分答案,但是它了不起的地方是一下子把所有的答案都二分出来了,从而可以一下子得出所有查询. CDQ分治通俗的讲就是二分查询.通常的做法是把所有的查询分成两半,然后通过递归先计算出左边一半的所有的查询,然后通过这些已知的左半边的值来更新右半边的值.这里,最最重要的思想是通过左半边来更新右半边.更具体一点,就是用左半边的修改来更新右半边的查询. 重要的事情说话三遍: CDQ分治就是通过左

[整体二分]【学习笔记】【更新中】

先小结一下吧 主要为个人理解 整体二分 理解 $zyz:$整体二分是在权值上进行$CDQ$分治 我觉得更像是说$:$整体二分是在答案上进行$CDQ$分治 整体二分是二分答案在数据结构题上的扩展 因为数据结构题二分的答案通常是第几个操作之后,需要进行一些操作(预处理)之后才能判断,所以每次询问二分还不如从前往后暴力找 整体二分可以解决这样的问题 核心就是维护一个$cur$数组保存当前的影响,分治到$[l,r]$时只需要计算$[l,mid]$的影响再与$cur$里的合并就好了 这样分治里的操作就只与

POJ 2104:K-th Number(整体二分)

http://poj.org/problem?id=2104 题意:给出n个数和m个询问求区间第K小. 思路:以前用主席树做过,这次学整体二分来做.整体二分在yr大佬的指点下,终于大概懂了点了.对于二分能够解决的询问,如果有多个,那么如果支持离线处理的话,那么就可以使用整体二分了. 在这题二分可行的答案,根据这个答案,把询问操作丢在左右两个队列里面分别递归继续按这样处理.注释里写的很详细. 1 #include <iostream> 2 #include <cstdlib> 3 #

整体二分初步

部分内容引自myt论文:树状数组延伸和离线优化(CDQ.整体二分和莫队) 大致思路 1.确定答案范围[L,R],mid=L+R>>1;2.算出答案在[L,mid]内的操作对答案在[mid+1,R]内的操作的贡献;3.将答案在[L,mid]内的操作放入队列Q1,solve(Q1,L,mid)将答案在[mid+1,R]内的操作放入Q2,solve(Q2,mid+1,R) 伪代码 divide(hd,tl,l,r){ if(hd>tl) exit if(l==r){ for(i->hd

整体二分初探 两类区间第K大问题 poj2104 &amp; hdu5412

看到好多讲解都把整体二分和$CDQ$分治放到一起讲 不过自己目前还没学会$CDQ$分治 就单独谈谈整体二分好了 先推荐一下$XHR$的 <浅谈数据结构题的几个非经典解法> 整体二分在当中有较为详细的讲解 先来说一下静态第$K$小的整体二分解法 $(POJ2104)$ 题目链接:http://poj.org/problem?id=2104 所谓整体二分 就是利用所有的询问相互独立而把它们$($此题没有修改操作$)$通过二分把它们分治进行处理 不妨先来考虑下一个简单易懂的$O(NlogS)$的排序