分块(简单版树状数组,线段树)

前言

首先,在NOIP的比赛里分块是一个很好的水分神器,因为它可以代替树状数组,线段树,但是如果出题人要卡你的程序的话......

分块思想

包含n个元素的整数数组A,每次可以 C(i, j) : 修改一个元素A[i] = j Q(i, j) : 询问A[i]+A[i+1]+…+A[j]的值

如何设计算法,使得修改和询问操作的时间复杂度尽量低? 显然存在修改O(1),查询O(n)的算法。

每次重新计算,一些未被修改区间也经常被重复求和,于是我们可以将A[1..n]均分成sqrt(n)块,每块内部的元素为sqrt(n)个。

修改操作时只需要修改A[i]及A[i]所在的块。

询问操作时是需要枚举i和j所在的块内部,及其之间的块。

时间复杂度在O(sqrt(n))级别。

主要思想就是将待操作的长度为N的区间分成大小为sqrt(N)的块,然后实现各种操作……

一些常用定义:

  MAGIC:定义一个块的大小,如字面意思,一个莫名其妙的数字……

  于是,我们把一段长度为N的区间,分成了若干长度为 MAGIC 的区间:[0,magic),[magic, 2magic)....

   于是易得,i / MAGIC 就是点 i 所在块的编号,若 i % MAGIC == 0,则证明由点 i 开始是一个新区间

一般来讲,我们在预处理和修改的时候,维护两个信息,一个是序列,另一个是块

例题

讲了这么多还是来看例题吧......

链接点我!!!

#6277. 数列分块入门 1

题目描述

给出一个长为 n的数列,以及 个操作,操作涉及区间加法,单点查值。

输入格式

第一行输入一个数字 n

第二行输入 n 个数字,第 i 个数字为 ai​​,以空格隔开。

接下来输入 n 行询问,每行输入四个数字 optlr、c,以空格隔开。

若 opt=0,表示将位于 [l,r] 的之间的数字都加 c

若 opt=1,表示询问 a的值(l 和 c 忽略)。

输出格式

对于每次询问,输出一行一个数字表示答案。

样例

样例输入

4
1 2 2 3
0 1 3 1
1 0 1 0
0 1 2 2
1 0 2 0

样例输出

2
5

数据范围与提示

对于 100% 的数据,1≤n≤50000,−231≤othersans≤231−1

 

题解:

  (建议大家边看程序边题解)

  这是一题区间修改,单点查询可以用树状数组,线段树(这不是放*吗

  但是我们在这里考虑分块;

  首先,我们开一个最普通的数组来记录每个元素的权值;

  然后再开一个数组来记录每个元素所对应的分块;

  然后再再(语文有点不好......)开一个数组来记录每个分块要加的值;

  每次寻找 l , r ,之间的区间,在分块内部的直接打上标记;

  如果在分块外面的直接暴力修改;

 

  嗯,代码......

 1 //NOIPRP++
 2 #include<bits/stdc++.h>
 3 #define Re register int
 4 using namespace std;
 5 int N,a[50005],f[50005],opt,l,r,c,Sqr,Tage[50005]; 
 6 inline void read(int &x){
 7     x=0; char c=getchar(); bool p=1;
 8     for (;'0'>c||c>'9';c=getchar()) if (c=='-') p=0;
 9     for (;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
10     p?:x=-x;
11 }
12 inline void Add(int l,int r,int c){
13     for (Re i=l;i<=min(f[l]*Sqr,r);i++) a[i]+=c;
14     if (f[l]^f[r]) for (Re i=(f[r]-1)*Sqr+1;i<=r;i++) a[i]+=c;
15     for (Re i=f[l]+1;i<=f[r]-1;i++) Tage[i]+=c;
16 }
17 int main(){
18     Re i,j;
19     read(N);Sqr=sqrt(N);
20     for (i=1;i<=N;i++) f[i]=(i-1)/Sqr+1;
21     for (i=1;i<=N;i++) read(a[i]);
22     for (i=1;i<=N;i++){
23         read(opt);read(l);read(r);read(c);
24         if (opt==0) Add(l,r,c);
25         if (opt==1) printf("%d\n",a[r]+Tage[f[r]]);
26     }
27     return 0;
28 }
29 //NOIPRP++
View Code

 

 这里由于博主懒,所以就不再讲其他扩展的例题了,如果有大佬还想要更多有关分块的好题的话请点我,当然也不能少了这些题目的题解......

posted @ 2018-11-06 21:37  to_the_end  阅读(168)  评论(0编辑  收藏  举报