尽人事,听天命。|

Jerrycyx

园龄:2年9个月粉丝:2关注:1

2025-03-06 16:06阅读: 1评论: 0推荐: 0

分块:2025.2 练习记录

P3372 【模板】线段树 1

虽然这是道线段树题,但是数据范围只有 \(10^5\),拿来作为分块模板题也未尝不可。

计算出段长以后,将数列按照这个段长划分(最后一段可以不满),同时记录每个数所属段的编号。

int len;
struct Block{
int l,r;
LL dat,add;
}bk[N];
int idx,bel[N];
...
len=sqrt(n);
bk[++idx].l=1;
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
if(i-bk[idx].l+1>len)
{
bk[idx].r=i-1;
bk[++idx].l=i;
}
bel[i]=idx;
bk[idx].dat+=a[i];
}
bk[idx].r=n;

然后对于每一个段,记录一个 add,表示该段被整体加上某个数的次数。

  • 修改:对于整段,直接加在 add 上;对于两侧非整段部分,则直接在原数组 a 上暴力累加。

  • 查询:对于整段,加上该段记录的 a 之和,再加上该段 add 值乘以段长;对于非整段,暴力累加 a,注意累加的每个 a 都需要加一下该段的 add 值。

这样,修改和查询的时间复杂度都是 \(O(\sqrt{n})\),总时间复杂度 \(O(n\sqrt{n})\)可过

注意:分块时一般都要特判左右边界在同一段内的情况并进行特殊处理,不要妄想你的代码能自己处理好这个情况。

P4168 [Violet] 蒲公英

题目要求求区间众数。

将数列分块以后,设查询区间为 \([l,r]\),其中连续整块为 \([L,R]\),则区间被分为了 \([l,L-1][L,R][R+1,r]\) 三部分,进而可以分为在整块内的部分 \([L,R]\) 和不在整块内的部分 \([l,L-1]\cup[R+1,r]\),显然众数只有可能是整块部分的众数或者非整块部分出现的数。

对于整块部分,因为块数为 \(\sqrt{n}\),所以可以直接预处理所有第 \(i\) 块到第 \(j\) 块的众数,总数为 \(O(n)\) 级别,运用双指针等技巧可以轻松做到 \(O(n\sqrt{n})\) 预处理。

对于非整块部分,考虑如何快速求出某一个数在某段内出现的次数。这个应该是经典技巧,我在这篇题解里就用到了:记录每一个数出现的位置并排序,通过两次二分查找找到这些位置中在所查找区间内的一段。这一段的段长就是所求的出现次数。

总时间复杂度 \(O(n \sqrt{n} \log n)\) 级别,理论上来说是会部分超时的(分块:一生之痛)。但是如果设块长为 \(l\),那么时间复杂度拆开应该是 \(O(l \times m \log n + \frac{n^2}{l})\),《算法竞赛进阶指南》上说取块长 \(l=\sqrt{\frac{n}{\log n}}\) 最佳(实测这样可过),我实测下来大约取 \(l=\frac{\sqrt{n}}{10}\) 左右最快,

这个最优块长的计算我现在暂时不会,到时候学会了到这里补上。

P10590 磁力块

建立一个 BFS 模型:每次取队头,用它来找可达磁石;每找到一块新磁石就将它入队。

这道题有两个参数:距离和质量。如果将所有磁石按照距离排序,那么距离可达磁石就是一段以 \(1\) 为左端点的确定区间,然而这段区间内并不是所有石头质量都满足条件。如果按照质量排序,那么距离又不都满足条件了。

我们需要找一个方法兼顾这两个参数,而这个是可以用分块实现的:将所有磁石先按照距离排序再分块,这样就可以保证块与块之间的距离参数是递增的;再对每一个块内按照质量排序,这样就可以保证块内磁石的质量参数是递增的。

这样,我们就兼顾了两个参数:从头连续多个整段内的磁石都满足距离参数,这些段内无需再考虑距离;这些整段内从头连续区间的磁石都满足质量参数,可以愉快地使用每次砍左边一段区间的方式避免重复扫描。最后一个非整段暴力处理的时间复杂度也是可以接受的 \(O(\sqrt{n})\)

posted @   Jerrycyx  阅读(1)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起