分块学习笔记

lyx 上课了!重新复习一下吧!

一个序列长度为 \(n\),然后 \(m\) 次单点修改,\(q\) 次前缀和

\(n\le 10^5,m\le 10^5,q\le 10^5\):直接分块,随便搞
\(m+q\sqrt{n}\):单点修改,前缀查就分一个块查
\(q+m\sqrt{n}\):我们的前缀和要 \(O(1)\),那么我们就维护这些块的前缀和,然后块内做前缀和,这个也是很好维护的

首先就是分块入门 9 题了!
以下都是 LOJ 的题
[P6277]
简单的
[P6278]
我们在块内对数排序,然后记下标。我们区间加分为整块和散块,然后整块就打标记,散块直接重构,然后块内二分
[P6279]
一样的
[P6280]
一样的
[P6281]
区间开方,对散点直接搞,然后维护块的最大值,如果最大值为1,那么就不用搞了,就和线段树维护这个东西差不多
[P6282]
我们使用块状链表,然后每次把一个元素插进去,如果块长 \(>\sqrt{n}\) 那么就把这个对半分
[P6283]
一样
[P6284]
一个块只有两个状态全都一样和没有变成全部一样,然后直接做即可,时间复杂度显然正确
[P6285]
有点意思!区间众数,一共有三个版本。学会最强的那个就可以了吧?

1.对序列分块,我们要处理出第 \(i\) 个块到第 \(j\) 个块的众数,首先我们第 \(i\) 个块就是单独开桶搞,然后我们 \(i-j\)$ 的答案就是从 \([i,j-1]\) 继承下来如果答案更新了,那么新的众数只可能是第 \(j\) 块里的数以及之前块的其中一个,所以我们要有前面的一个桶继承下来.然后再得出新的众数,这样是 \(O(n\sqrt{n})\) 的。这其实每次从一个块开始往后暴力维护众数就可以了,前面说的有点烦。然后考虑查询,我们新的众数只可能原本的众数和这一块的众数,所以我们。但是我们存不下这个桶啊!不会做!

我们查询的话,就是一个整块和一些散块,然后一个数如果能成为众数。我们的答案最多增大 \(2\sqrt{n}\)。然后我们枚举边上的元素,判断能否到更高。我们要的是根号,不能比这个更高了。然后是不是做完了啊!我们从左向右遍历,如果当前这个数符合,那么就对答案 while 暴力加,遍历完,那么就做完了。四倍经验!啊。真的把以前的思路忘掉了。我们这个东西其实是一个判定的东西了,如果我们判定 \(x\),那么就得判定 \(pos[x]+ans\) 是否在这个区间里面,这个显然是好判断的,然后就做完了。对于左边的元素就是这样,对于右边的元素,聪明的小朋友肯定要说,直接反过来做!其实直接 \(pos[x]-ans\) 即可。有点弱智了

[poj:Chef and Churu]
单点修改,函数是一个区间和,然后询问函数区间和。我们对函数进行分块,然后函数里面要怎么排序一下。然后询问区间函数和就是直接散块暴力加,然后再维护整个块的和。然后单点修改将 \(a[x]=y\),我们枚举块,然后对于每个块把右端点 \(\ge x\) 都改一下,然后直接统计有多少个即可,然后对于左端点也同理,如果我们要访问一个块,那么我们只要记录一下函数左右端点排序后的下标即可,这个是 \(O(n\log n+n\sqrt{n})\) 的可过!说的不是特别清楚,我们首先有一个差分后缀加,我们直接开个通记录数值的下标,然后找到这个位置,就可以很方便维护整体和,然后在维护一下差分数组。要访问单点值怎么办呢?我们直接把差分数组 \(push\_ down\) 即可

[P1494]
要求这个概率,我们直接就是用总的除以总的。然后我么就是要求区间有多少方案使得选出的两个颜色香同,这个显然好维护,然后就做完了。
这个莫队是怎么排序来着(

[P4867]
莫队+分块
我们莫队有 \(O(m\sqrt{n})\) 次单点加数和 \(m\) 次区间询问。我们使用分块维护这个即可。

[P4135]
在线莫队吗?有点意思
考虑我们莫队离线的排序是为了限制双指针的移动次数,在线的话我们也要这样做。我们要维护这个桶。我们对于序列分块,然后对于每个块都开一个桶并存下答案,对于每个询问我们只要。哦,我们先求出第 \(i\) 个块到第 \(j\) 个块的答案,然后我们对于每于 \([l,r]\),我们先统计出中间的答案,然后对于旁边的数暴力搞?定义 \(ll,rr\) 为分块的块的左边界和右边界,那么如果一个数在 \([l,ll]\)\([rr,r]\) 中出现奇数次并且在 \([ll,rr]\) 中出现偶数次,那么就把贡献更新一下,其他的情况同理,所以我们只要维护每个块 \(i\) 出现了多少次和每个块中每个数出现的前缀次数,类似一个三维数组 \(a[i][j][k]\) 代表 \(i\)(块内离散化)在第 \(j\) 个块中,\(k\) 位置前的出现次数。
其实还是没有考虑清楚的,因为我们需要枚举 \(\sqrt{n}\) 个数,然后快速求出 \(l,r\) 中的出现次数和 \(id[l],id[r]\) 中的出现次数。这个我们还要开一个桶。

这不是在线莫队啊,确实和莫队本质有区别了,重点在于中间块的跳跃这种的

[codechef 的一题]
判断集合重构,我们只需要给每个出现次数都随机个权值即可,然后加法哈希套个莫队维护一下就做完了。具体而言,这个东西是在线的,然后我们直接分块,整块暴力维护块的 \(sum\) 直接加,然后散块也加上去,然后就做完了。不放心可以使用双哈希。具体而言,我们得用分块维护一下 \(sum\) 啊,然后旁边怎么加呢?我们需要找到一段块中一个数的次数,然后旁边开桶,然后直接

对值域分块,课上没听懂

离线,在线维护区间逆序对数?以后补吧!先把前面好写的写了再说

[P4887]
学习莫队二离!只能算是一个技巧吧

拆位,转化一下就变成了有 \(k\) 位相同。还是老套路,我们处理出 \(l,r\) 块的答案,然后对于散的数找到其中的答案。

考虑使用 dp。我们可不可以。但是这个有点太蠢了

注意到值域非常小,我们 \(k\) 个位置相同的方案有 \(c(7,14)\) 种,也就是 3432 种。然后我们对于这个判定一下?容斥?先简单一点。\(O(n)\) 判定,或者更高怎么判定。我们扫一遍这个数组,开个桶,然后对于每一个数,然后花3432 的时间判定一个数。

我们拓展一个数,那么就花 3432 的时间判定一下,这个开个桶很容易解决。然后现在我们就相当于有 \(O(m\sqrt{n})\) 个加数和减数,然后 \(O(m)\) 次查询,然后把这个离线?感觉有这个感觉是得用。

看题解,容易发现我们更新出的题意很难做,而我们恰好抛弃掉了一个最重要的性质。我们莫队加一个数和减一个数都是求一段区间对一个数的答案的贡献。那么显然这个贡献是可减的,所以我们只要求 \([1,r]\sim [1,l-1]\) 即可,那么我们对于这个排序。我们的询问就变成了一个区间对一个数的答案贡献。但是比较。

我们还是抛掉了莫队挂询问的性质。我们一次的右端点询问对答案的贡献就是 \(\sum {f([1,r1],x)-f([1,l1-1],x)}\) 然后我们拆一下,发现询问分为两类

\[\sum_{i=l}^{r-1} f([1,i],x_{i+1}) \]

\[\sum_{i=l}^{r-1} f([1,l1-1],x_{i+1}) \]

第一类我们可以预处理,然后前缀和,考虑第二类。
我们发现我们在拓展 \([1,r]\) 的时候动态维护 \([1,16384]\) 所有数的答案即可,然后就做完了,就是我们每次拓展一个数的时候加上 \(3264\) 个数的代价。
还有一些细节的问题。我们找到所有符合条件的数,因为异或的交换律然后再异或回去就行了,做完。不是,我这个做法是不是都不用对询问排序了?是不是甚至我们都不用莫队了?对啊!不对啊,我们要保证类型二的询问个数为 \(O(n\sqrt{n})\) 的啊。

[P3709]
这个体面乱七八糟的,想想这个题面在说什么。我们发现这个其实就是每次取出一个上升子序列,然后就是区间众数了,有点搞笑。

[P4278]
学习对值域分块!

树套树模板让我们做几件事情,查询x在区间中的排名,查询区间排名为 \(k\) 的树,区间前驱和后继。我们对值域分块,那么对于查排名,我们只要维护块中有多少个数,然后在最后那个块里面扫一遍加一下即可。对于区间,这个就比较困难了。然后其他应该像这样也好做,有什么用呢?
题解里有一句话很神:

分块我们只要询问在这个块当中有几个数字恰好为 \(x\),第 \(i\) 个值域块里有几个数。考虑把这个东西放到序列上去询问,那么我们就对序列分块,然后区间就变成了整块和散块。

区间有几个数恰好为 \(x\),单点修改,我们对序列分块,然后每个块开一个桶,这样就做完了。对于散块,我们暴力搞。具体而言,就是维护这个序列(就是直接在数组里面单点修改)
区间里在一个值域块里面有多少个数,单点修。我们还是对序列分块,我们这个单点修就要下点功夫了。我们区间里面要离散化,然后我们维护区间桶的前缀和,因为我们离散化了,所以维护前缀和就是 \(O(\sqrt n)\),然后我们只要开个映射数组 \(id\) 就可以快速将值对应到桶,然后就可以 \(O(1)\) 查询了。同样的,对于散块,我们直接暴力不对不对,我们这个是要 \(O(1)\) 的啊。

我们维护区间前缀值域块有多少个数是简单的,和上面差不多。然后我们维护区间有几个数恰好为 \(x\),我们维护前缀区间一个数的出现次数,然后维护一个序列块中一个数的出现次数.这下真没问题了,然后感觉还是比较好写的,好写就有鬼了!

然后就是一道黑题,确实分块还是很好玩的,每道题都不太一样。

带插入、修改的区间 \(k\) 小值在线查询。

带插入的我们可以使用块状链表来维护,此时就是一个非常自然的对序列分块。区间 \(k\) 小?直接用上面的方法维护即可?

所以我们要维护一个块状链表
我们这个是对序列分块,然后我们要支持对于区间查询有多少个属于一个值域块的数和单点有多少数。区间单点有多少个数等于x。我们可以直接对于每个序列块都开一个桶因为权值 \(\le 70000\),所以是开的下的。然后这个桶代表前缀块这个数的出现次数。那么这样我们修改就是 \(O(\sqrt{n})\) 的。然后我们还得维护一个值域块内有多少数。这个怎么办?我们对于每一个块,还有一个前缀值域块的桶就是对于每个值域块还有一个前缀和,这个也是 \(O(len)\) 的,感觉很显然了,将两个块分开啊,我们暴力分开,可以证明分开的次数不会超过 \(O(len)\) 的啊?我们要寻找一个方式使得时间平衡。我们有两个操作,一个是将块分开,我们最坏的情况就是将每次都往长度最大的那个块加数,那么我们一共会操作 \(\frac{2n}{len}\) 次,然后每次暴力重构从前面一个块继承下来即可。那么我们这个时间是 \(O(n)\) 那么我们将块长稍微调小一点感觉会快一点。算了,先不考虑这个了。然后就做完了。我们每次加数就是直接在块里加,然后外面再有一个链表。
我们还要维护哪个数在哪个块中,这个显然好维护。
然后发现一个特别蠢的东西。我们显然可以在外面开两个桶,这样就可以花 \(O(\sqrt{n})\) 的时间把散块值域块的数的数量都可以 \(O(1)\) 求了,所以我们还是只用维护整块的信息

容易发现我们的作诗也可以这么写,这个真的就是人傻常熟大啊,我真的服了,这个莫名其妙的常数就是这么来的,真的怒了啊
思路等写了代码再说吧

[P4119]
区间赋值,区间 \(k\) 大,区间 \(k\) 大我们还是散块开桶然后整块区间查。我们要维护区间块的值域块前缀和,和单点值前缀和。

因为只有一种询问,我们把这个还是拆成散块和整块。我们考虑对整块直接上 \(tag\),然后对于散块重构,但是这样的话我们后面的前缀和影响太大了,怎么办呢?我们 \(push\_ down\) 也得是 \(O(1)\) 的啊。

因为是区间赋值,所以我们的这个东西是不是不会太大。我的键盘怎么坏了啊。好的,看来是洛谷的问题。我们重构完之后我们暴力搞对后面的贡献就是区间颜色种数乘根号。貌似不行啊。题目看错了

看题解,给点提示吧

将一个块的所有 \(x\) 变成 \(y\),但是我们还要查询单点值啊,这个不好搞啊。但是!分讨,如果一个区间既有 \(x\),又有 \(y\),那么我们进行这个操作会让颜色 \(-1\),然后如果没有 \(y\),有 \(x\),那么感觉比较可做。那么我们对于每一块的操作次数都不会超过 \(O(\sqrt{n})\) 然后还有询问带来的修改最多只会增加 \(O(m)\) 种颜色,所以我们显然是可以对每一块重构的吗?对的,可以每次花 \(O(\sqrt{n})\) 重构。然后就是将一个颜色换成另一种颜色这个操作了,哦对对对。我们还得维护区间前缀颜色和啊。

做不出来了

我们暴力重构每个块是 \(O(\sqrt{n})\) 才能让时间复杂度对,我们把这个处理好,然后相当于一个颜色减掉,一个颜色加上,这个是简单的,我们的前缀和也容易维护,只要先把这个颜色差分,然后每块暴力改,然后再合并即可。这个是对的。这个方法对于所有都适用吗?感觉是对的。这样我们可以很轻松维护每个块的颜色前缀和。然后我们要搞一个独立的东西出来维护每一个位置的具体值。

对于第二种情况我们直接暴力,对于第一种,我们有一个颜色替换,这个我们就可以使用并查集,维护区间颜色的归属。对于散块,我们可不可以暴力重构呢?会T。我们直接重构。不使用并查集了,垃圾算法。我们重构构 \(id\) 是不是就行了,感觉没问题欸,先 \(push\_ down\),然后再改。

posted @ 2024-08-17 11:17  wuhupai  阅读(22)  评论(0)    收藏  举报