分块——优雅的暴力

news/2025/3/19 11:33:23/文章来源:https://www.cnblogs.com/williamYcY/p/18231997

下面介绍一种暴力,当然呢这种暴力比一般快很多。
先说一下这个暴力的思路。对于一个长度为\(n\)的数组\(a\),可以把数组\(a\)分成\(k\)块,其中每一块的长度为\(len\),当然最后一行除外因为\(n\)可能不是\(k\)的倍数,最后一块的长度可以不是\(len\)
那么就可以用这些块来维护数据。
那么对于一个区间\([l..r]\)可以分成两种情况:
\(1\).区间\([l..r]\)在同一个块里也就是:
image
这种情况下可以暴力枚举\(l\)\(r\)即可。
\(2\).区间\([l..r]\)不在同一个块里也就是:、
image
这种情况下首先应该考虑中间整块的部分(图中\(3,4f\)),然后就是\(l\)\(r\)中的残块也就是\(2\)\(5\)中间的那一部分。

例题I 线段树1

点击查看题面 ## 题目描述

如题,已知一个数列,你需要进行下面两种操作:

  1. 将某区间每一个数加上 \(k\)
  2. 求出某区间每一个数的和。

输入格式

第一行包含两个整数 \(n, m\),分别表示该数列数字的个数和操作的总个数。

第二行包含 \(n\) 个用空格分隔的整数,其中第 \(i\) 个数字表示数列第 \(i\) 项的初始值。

接下来 \(m\) 行每行包含 \(3\)\(4\) 个整数,表示一个操作,具体如下:

  1. 1 x y k:将区间 \([x, y]\) 内每个数加上 \(k\)
  2. 2 x y:输出区间 \([x, y]\) 内每个数的和。

输出格式

输出包含若干行整数,即为所有操作 2 的结果。

样例 #1

样例输入 #1

5 5
1 5 4 2 3
2 2 4
1 2 3 2
2 3 4
1 1 5 1
2 1 4

样例输出 #1

11
8
20

提示

对于 \(30\%\) 的数据:\(n \le 8\)\(m \le 10\)
对于 \(70\%\) 的数据:\(n \le {10}^3\)\(m \le {10}^4\)
对于 \(100\%\) 的数据:\(1 \le n, m \le {10}^5\)

保证任意时刻数列中所有元素的绝对值之和 \(\le {10}^{18}\)

【样例解释】

现在这一题是要支持区间求和,很显然用分块来维护区间和,那么可以用一个数组\(s\)表示当前块的最大和。
对于每一次的询问仍旧分为两部分\(l\)\(r\)在同一块里和\(l\)\(r\)不在同一块里。
那么代码就是:

int query(int l,int r){int sid=id[l],eid=id[r];//表示开始块编号和结束块编号if(sid==eid){//l,r在同一块里int sum=0;for(int i=l;i<=r;i++)sum+=a[i]+b[sid];//这里的a是原数组,b是每一块的懒标记return sum;}int sum=0;for(int i=l;id[i]==sid;i++)sum+=a[i]+b[sid];//l所对应的残块for(int i=sid+1;i<eid;i++)sum+=s[i];//l,r中间的真快,s是每一块的和for(int i=r;id[i]==eid;i--)sum+=a[i]+b[eid];//r所对应的残块return sum;
}

修改的方法和询问是一样的

void add(int l,int r,int x){int sid=id[l],eid=id[r];if(sid==eid){for(int i = l;i <= r;i ++ )s[sid] += x,a[i] += x;return;}for(int i = l;id[i] == id[l];i ++ ) s[id[l]] +=x,a[i] +=x;for(int i = sid + 1;i < eid;i ++ )s[i] += len * x,b[i] += x;for(int i = r;id[i] == id[r];i -- ) s[id[r]] +=x, a[i] += x;
}

完整代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+5;
int a[N],b[N],s[N];
int id[N];
int n,m,len;
int query(int l,int r){int sid=id[l],eid=id[r];if(sid==eid){int sum=0;for(int i=l;i<=r;i++)sum+=a[i]+b[sid];return sum;}int sum=0;for(int i=l;id[i]==sid;i++)sum+=a[i]+b[sid];for(int i=sid+1;i<eid;i++)sum+=s[i];for(int i=r;id[i]==eid;i--)sum+=a[i]+b[eid];return sum;
}
void add(int l,int r,int x){int sid=id[l],eid=id[r];if(sid==eid){for(int i = l;i <= r;i ++ ){s[sid] += x;a[i] += x;}return;}for(int i = l;id[i] == id[l];i ++ ) s[id[l]] +=x,a[i] +=x;for(int i = r;id[i] == id[r];i -- ) s[id[r]] +=x, a[i] += x;for(int i = sid + 1;i < eid;i ++ ){s[i] += len * x;b[i] += x;}
}
signed main(){cin>>n>>m;len=sqrt(n);for(int i=1;i<=n;i++){cin>>a[i];int cnt=(i-1)/len+1;id[i]=cnt;s[cnt]+=a[i];}while(m--){int ops,l,r,c;cin>>ops>>l>>r;if(ops==1)cin>>c,add(l,r,c);else cout<<query(l,r)<<'\n';}return 0;
}

例题II

点击查看题面 # Anton and Permutation

题面描述

有一个长度为 \(n\) 的排列,初始为 \(1,2,\dots,n\)

现在对其进行 \(k\) 次操作,每次操作都是交换序列中的某两个数。对于每一个操作,回答当前序列中有多少个逆序对。

样例 #1

样例输入 #1

5 4
4 5
2 4
2 5
2 2

样例输出 #1

1
4
3
3

样例 #2

样例输入 #2

2 1
2 1

样例输出 #2

1

样例 #3

样例输入 #3

6 7
1 4
3 5
2 3
3 3
3 6
2 1
5 1

样例输出 #3

5
6
7
7
10
11
8
首先可以先把分块数组逆序对的个数初始化为$0$ 每次交换a[l]与a[r],对序列的逆序对个数影响有多大?我们设[l+1,r-1]区间内比 a[l]小的元素有Sl个,比a[l]大的有Bl个,比a[r]小的元素有Sr个,比a[r]大的有Br个。 则逆序对的个数ans+=(Bl-Sl)+(Sr-Br)=(r-l-1-2*Sl)+(Sr-(r-l-1-Sr))=2*(Sr-Sl)也就是说,每次给出询问[l,r],我们需要求出[l+1,r-1]区间内的Sl和Sr另外,还需要单独判断一下a[l]和a[r]的大小关系,若a[l]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/720083.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[转]第48章:SEH - Rev

SEH 是 Windows 操作系统提供的异常处理机制,在程序源代码中使用 __try __catch __finally 等关键字来具体实现。 进程在运行过程中发生异常,OS 会委托进程处理,但如果进程内没有具体实现 SEH ,那么 OS 会启动默认的异常处理机制,终止进程运行。如果有调试器,则先交由…

Kubernetes - 安装方法

Minikube: 对于想要在系统上安装 Kubernetes 但系统资源有限的用户来说,它是理想的选择。因此,minikube 的关键点在于它没有单独的 Kubernetes 主节点和 Kubernetes 工作节点架构。在这里,我们将所有 Kubernetes 组件打包在一起作为一体化设置。单个系统同时充当主节点和工…

UE4 AI

UE4中的AI UE4中AI一般通过行为树来实现行为树控制了AI的动作,而该执行哪些动作则是由AIController(本质上是一个类)进行判断,然后传入BlackBoard的Key中,之后在行为树里利用Selector或者Sequence进行执行对应的动作 每个Actor类都可以选择一个AIController类 行为树中的两个…

一文了解JVM(中)

HotSpot 虚拟机对象探秘 对象的创建Header 解释使用 new 关键字 调用了构造函数使用 Class 的 newInstance 方法 调用了构造函数使用 Constructor 类的newInstance 方法 调用了构造函数使用 clone 方法 没有调用构造函数使用反序列化 没有调用构造函数说到对象的创建,首先让我…

FFT 学习笔记

多项式 复数 单位根 DFT IDFT FFTFFT 学习笔记 1.多项式与卷积 1.1 多项式 对于多项式 \(F(x)=a_0+a_1x+a_2x^2+a_3x^3+\dots+a_nx^n\),我们称 \(a_0,a_1,\dots,a_n\) 为它的系数,这种表示法叫做系数表示法。 定义 \(F(x)\) 的 \(n\) 次项系数为 \(f_n\)。 我们有: \[F(x)=…

题目集4-6的总结性Blog

一.前言: 在这几周,我们又进行了3次pta的题目训练。 首先是答题程序的最后一次迭代,答题程序-4,接着就是新的迭代,家居电路模拟程序。经过一段时间的学习,我对面向对象设计的理解进一步加深,这三次题集写起来也没有之前那么困难了,虽然还有不足,我仍在一次次答题中学…

Kubernetes – 架构

Kubernetes 集群主要由称为节点的工作机器和控制平面组成。集群中至少有一个工作节点。Kubectl CLI 与控制平面通信,控制平面管理工作节点。 Kubernetes – 集群架构 如下图所示,Kubernetes 采用客户端-服务器架构,有主节点和工作节点,主节点安装在单个 Linux 系统上,而节…

九、FreeRTOS学习笔记-列表和列表项

列表和列表项的简介 列表是 FreeRTOS 中的一个数据结构,概念上和链表有点类似,列表被用来跟踪 FreeRTOS中的任务。 列表项就是存放在列表中的项目列表相当于链表,列表项相当于节点,FreeRTOS 中的列表是一个双向环形链表 列表的特点:列表项间的地址非连续的,是人为的连接到…

我真的从测试转成了开发......

写在前面 因为走的圈太大了,早上上班差点迟到,幸好有我每日5公里的加持,侥幸踩点进办公室,哈哈,真的好险! 我开发的功能不能用了 上午开始着手某功能的开发,还在写后台逻辑。 结果到了下午,由于前端同学的代码冲突,打包发布后,导致我写的功能直接不能用了,瞬间emo了…

手把手教你用VM搭建Linux系统

手把手教你用VM搭建Linux系统一、安装vm 查看是否安装成功,打开网络适配器(win+R+ncpa.cpl) 确保有 VMnet1 和 VMnet8二、创建虚拟机step01step02step03 密码123456(我怕我忘了),全名是对你的虚拟机的别称没什么太大作用,用户名代表你说什么用户会涉及到权限step04,位置…

kettle从入门到精通 第六十五课 ETL之kettle 执行动态SQL语句,轻松实现全量增量数据同步

本次课程的逻辑是同步t1表数据到t2表,t1和t2表的表机构相同,都有id,name,createtime三个字段。 CREATE TABLE `t1` (`id` bigint NOT NULL AUTO_INCREMENT,`name` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,`createtime` datetime DEFAUL…

HMI-Board之LVGL应用

LVGL应用示例移植 使用默认模板工程新建一个RT-Thread项目,BSP版本为1.1.1打开RT-Thread Settings,点击右侧箭头按钮进入详细页,在硬件栏开启以下几个配置选项(LCD、触摸屏、demo)此时,打开board文件夹,发现下面会有一个lvgl的目录,package目录下会有LVGL和lv-music两个…