P2801 教主的魔法

devtools/2024/10/19 3:33:43/

[题目通道](教主的魔法 - 洛谷)

摘要

分块,是一种优雅的暴力,它通过对数列分段,完成对数列一些区间操作和区间查询的操作,是一种根号算法。

这篇学习笔记&题解是本萌新在学习分块过程中的一些感悟,希望能够帮助分块零基础的同学学会基础分块。


0 说明

本文中,以下变量有特定的含义:

  • block⁡block:块的大小
  • nn:被分块的数列的大小(长度)
  • LxLx​:第 xx 号块的左边界
  • RxRx​:第 xx 号块的右边界
  • tot⁡tot:块的数量
  • belong⁡xbelongx​:第 xx 号元素所属的块

在写作时,由于本萌新的失误,只好提前在这里令 [l,r][l,r] 与 [x,y][x,y] 等价。


1 建块

1.1 建块需要完成的任务

在读入数据后,建块需要完成以下几个任务:

  • 确定块的大小
  • 确定块的数量
  • 标记每个块的左右边界
  • 标记每个元素所属的块
  • 对每个块的数据进行初始化

1.2 确定块的大小

一般来说,我们习惯于令 block⁡=nblock=n​。

但是由于毒瘤良心命题人泛滥,block⁡=nblock=n​ 极其有可能被针对,在这种情况下,我们可以对块的大小适当作出一些调整,例如 n+1n​+1,n−1n​−1,nlg⁡(n)lg(n)n​​ 等。

一般这个工作只有一句话:

block = (int)sqrt((double)n);

1.3 确定块的数量

在确定了块的大小后,块的数目就很容易确定了。

但是 nn 不一定是一个完全平方数,我们需要把最后几个无法凑足 block⁡block 个元素的再单独分一个块。

代码如下:

tot = n / block;
if(n % block) tot++;

1.4 标记每个块的左右边界

非常显然,L1=1,R1=block⁡,L2=block⁡+1,R2=2×block⁡,⋯L1​=1,R1​=block,L2​=block+1,R2​=2×block,⋯

从而可以得出结论:

Lx=(x−1)⋅block⁡+1,Rx=x⋅block⁡Lx​=(x−1)⋅block+1,Rx​=x⋅block

特别地,Rtot⁡=nRtot​=n

代码:

for(int i = 1; i <= tot; i++){L[i] = (i - 1) * block + 1;R[i] = i * block;
}
R[tot] = n;

1.5 标记每个元素所属的块

根据 1.4,我们很容易推出公式如下:

belong⁡x=x−1block⁡+1belongx​=blockx−1​+1

代码如下:

for(int i = 1; i <= n; i++)belong[i] = (i - 1) / block + 1;

重要:在使用分块过程中,一定要注意区分 tot⁡tot 和 nn。 tot⁡tot 是块的总数,nn 是原来元素的总数。

1.6 对每个块的元素进行初始化

这项工作因题目不同而不同,如【教主的魔法】一题,就要对每个块的元素进行排序。

因为排序会对原始数列作出改变,所以在本题中,应当先把数列复制一遍再进行分块


2 分块题常见的操作

修改:

  • 对数列 [l,r][l,r] 内的每个数加上 kk
  • 对数列 [l,r][l,r] 内的每个数减去 kk
  • etc.

查询:

  • 求数列 [l,r][l,r] 内的所有数的和
  • 求数列 [l,r][l,r] 内的数有多少大于/小于/大于等于/小于等于 kk
  • etc.

3 修改操作

考虑两种修改操作本质相同,第二种修改操作相当于第一种修改操作中 k=−k′k=−k′。

3.1 暴力修改

考虑枚举区间 [l,r][l,r] 之间所有数,直接对其实施修改,在修改的过程中维护每一个块的和/大小关系等。

但这不是我们考虑的东西

3.2 考虑线段树思想

线段树一个重要思想:lazytag

考虑应用在分块中。在修改操作中,如果是整块,就不维护每个的具体信息,而是在这个块的 lazy⁡lazy 标记上加上 kk。对于没有整块修改的部分(即块 belong⁡xbelongx​ 和 belong⁡ybelongy​ 的修改部分),暴力修改。

这样的话,第 ii 个数据 aiai​ 的真正数据值为 ai+lazy⁡belong⁡iai​+lazybelongi​​。

如果询问涉及到排序,块 belong⁡xbelongx​ 和 belong⁡ybelongy​ 需要全部重新备份和排序,对于块 [belong⁡x+1,belong⁡y−1][belongx​+1,belongy​−1] 的块,数的相对大小不会改变,所以可以不重新排序。

特别地,需要特判 belong⁡x=belong⁡ybelongx​=belongy​ 的情况。

代码:

void change(){if(belong[x] == belong[y]){for(int i = x; i <= y; i++){a[i] += k;sum[belong[x]] += k;}return;}for(int i = x; i <= R[belong[x]]; i++){a[i] += k;sum[belong[x]] += k;}for(int i = L[belong[y]]; i <= y; i++){a[i] += k; sum[belong[y]] += k;}for(int i = belong[x] + 1; i <= belong[y] - 1; i++){lazy[i] += k;sum[i] += blo * k;}
}

对以下这句代码作出特别解释:

sum[i] += blo * k;

不用特判最后一块的原因是:如果操作区间覆盖到的最后一块,也一定是作为 belong⁡ybelongy​ 处理掉了,剩下来的块长一定是 block⁡block。


4 查询操作

4.1 查询元素和

对于块 belong⁡xbelongx​ 和 belong⁡ybelongy​,暴力枚举加和,注意加上其元素后还要加上 lazy⁡belong⁡ilazybelongi​​

对于 [belong⁡x+1,belong⁡y−1][belongx​+1,belongy​−1] 的块,直接 ans=ans+sum[i] 即可。

同样的,需要特判 belong⁡x=belong⁡ybelongx​=belongy​

代码:

int query_sum(){int ans = 0;if(belong[x] == belong[y]){for(int i = x; i <= y; i++){ans += a[i] + lazy[belong[x]];}return ans;}for(int i = x; i <= R[belong[x]]; i++){ans += a[i] + lazy[belong[x]];}for(int i = L[belong[x]]; i <= y; i++){ans += a[i] + lazy[belong[y]];}for(int i = belong[x] + 1; i <= belong[y] - 1; i++){ans += sum[i];}return ans;
}

4.2 查询关系

与4.1类似,在块 belong⁡xbelongx​ 和 belong⁡ybelongy​,暴力枚举求答案;

对于 [belong⁡x+1,belong⁡y−1][belongx​+1,belongy​−1] 的块,因为其是有序的,进行二分找到端点位置,然后加加减减求出块中有多少符合要求的元素即可。

本处代码见5.


5 教主的魔法

在学习完分块后,我们可以发现,教主的魔法就是一道裸的分块题。

因此,完整代码如下:

#include<bits/stdc++.h>
#include<vector>
using namespace std;
int m,n,t,pos[1251000];
int s[2151000],flag[1251000];
vector<int>v[550000];void reset(int x) {v[pos[x]].clear();for(int i=(pos[x]-1)*m+1; i<=min(pos[x]*m,n); i++)v[pos[x]].push_back(s[i]);sort(v[pos[x]].begin(),v[pos[x]].end());
}void change(int a,int b,int c) {for(int i=a; i<=min(pos[a]*m,b); i++)s[i]+=c;reset(a);if(pos[a]!=pos[b]) {for(int i=(pos[b]-1)*m+1; i<=b; i++)s[i]+=c;reset(b);}for(int i=pos[a]+1; i<=pos[b]-1; i++)flag[i]+=c;
}int query(int l,int r,int c) {int ans=0;for(int i=l; i<=min(pos[l]*m,r); i++)if(s[i]+flag[pos[l]]<c)ans++;if(pos[l]!=pos[r]) {for(int i=(pos[r]-1)*m+1; i<=r; i++)if(s[i]+flag[pos[r]]<c)ans++;}for(int i=pos[l]+1; i<=pos[r]-1; i++) {int x=c-flag[i];ans+=lower_bound(v[i].begin(),v[i].end(),x)-v[i].begin();}return ans;
}signed main() {scanf("%d %d",&n,&t);m=sqrt(n);for (int i=1; i<=n; i++) pos[i]=(i-1)/m+1;for (int i=1; i<=n; i++) scanf("%d",&s[i]);for(int i=1; i<=n; i++)pos[i]=(i-1)/m+1,v[pos[i]].push_back(s[i]);for(int i=1; i<=pos[n]; i++)sort(v[i].begin(),v[i].end());for (int i=1; i<=t; i++) {int a,b,c;char x;cin>>x;scanf("%d%d%d",&a,&b,&c);if (x=='M') {change(a,b,c);} else if (x=='A') {cout<<b-a+1-query(a,b,c)<<endl;}}return 0;
}


http://www.ppmy.cn/devtools/95091.html

相关文章

web页面的性能测试

背景 测试大模型主要web页面的性能 使用工具 通过google自带的lighthouse测试页面的性能 各个参考指标 First Contentful Paint(FCP):测量在用户导航到页面后浏览器呈现第一段 DOM 内容所花费的时间。页面上的图像、非白色<canvas>元素和 SVG 被视为 DOM 内容&#…

Python上机_1

题目1&#xff1a;创建一个数值范围为0~1&#xff0c;间隔为0.01的数组&#xff0c;并查看该数组的维度。 代码如下&#xff1a; import numpy as np arrnp.arange(0,1,0.01) print(创建的数组为&#xff1a;,arr) print(数组的维度为&#xff1a;,arr.ndim) 题目二&#xf…

GDB:wrong library or version mismatch?

实际项目中&#xff0c;常常会遇到程序运行环境与本地环境不一致的情况。一般GBG会报如下错误&#xff1a; warning: .dynamic section for "/lib64/librt.so.1" is not at the expected address (wrong library or version mismatch?) 。。。。。。 Do you need &q…

智能分析/视频汇聚EasyCVR安防视频融合管理云平台技术优势分析

安防行业的发展历程主要围绕视频监控技术的不断改革升级&#xff0c;从最初的模拟监控到数字监控&#xff0c;再到高清化、网络化监控&#xff0c;直至现在的智能化监控&#xff0c;每一次变革都推动了行业的快速发展。特别是近年来&#xff0c;随着AI、大数据、物联网等技术的…

中国式报表有这么多种类型,你都知道吗?

中国式报表是一种在中国企业中使用的会计报告格式&#xff0c;但你真的了解它吗&#xff1f;你知道它有多少种类型吗&#xff1f;今天我们就一起来聊聊&#xff0c;中国式报表都包含哪些类型的报表吧&#xff01; 按样式来划分&#xff0c;中国式报表通常分为以下几类&#xff…

FreeRTOS的两种启动方式及在不同位置开启调度器的区别

启动方式 所谓的启动方式&#xff0c;其实就是你写代码创建任务的习惯&#xff0c;主要有以下两种&#xff1a; 第一种&#xff0c;在主函数里边创建一个任务&#xff0c;然后在这个任务里边创建其他任务&#xff0c;任务创建完成后删除起始任务。 第二种&#xff0c;在主函数…

网页版IntelliJ IDEA部署

在服务器部署网页 IntelliJ IDEA 引言 大家好&#xff0c;我是小阳&#xff0c;今天要为大家带来一个黑科技——如何在云端部署和使用WEB版的IntelliJ IDEA&#xff0c;让你在任何地方都可以随心所欲地进行Java开发。这个方法特别适合那些用着老旧Windows电脑&#xff0c;部署…

PHP项目任务系统小程序源码

&#x1f680;解锁高效新境界&#xff01;我的项目任务系统大揭秘&#x1f50d; &#x1f31f; 段落一&#xff1a;引言 - 为什么需要项目任务系统&#xff1f; Hey小伙伴们&#xff01;你是否曾为了杂乱的待办事项焦头烂额&#xff1f;&#x1f92f; 或是项目截止日逼近&…