题解【loj6277】数列分块入门1

题目描述

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

输入格式

第一行输入一个数字\(n\)

第二行输入\(n\)个数字,第\(i\)个数字为\(a_{i}\),以空格隔开。

接下来输入\(n\)行询问,每行输入四个数字\(opt\)\(l\)\(r\)\(c\),以空格隔开。

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

\(opt = 1\),表示询问\(a_{r}\)的值(\(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

数据范围与提示

对于所有的数据,\(1 \leq n \le 50000\)\(-2^{31} \leq others、ans \le 2_{31} - 1\)

题解

这是一道很好的分块入门题。

所谓分块,就是一种通过将一个序列分成多块后,在每块上打标记以实现快速区间修改,区间查询的一种算法。其均摊时间复杂度为\(\Theta\sqrt{n}\)

在一般情况下,每个块的长度都为\(\sqrt{n}\)

分块,被尊称为优雅的暴力,因此它的代码难度也不算高。总之,比线段树、树状数组等毒瘤数据结构的代码难度低。

我们需要建立三个数组:

  • \(a[]\),为题目中输入的序列;
  • \(b[]\),记录每个序列中的每个数在那一块;
  • \(add[]\),为序列的标记数组。

话不多说,上代码。

代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cctype>//头文件准备

using namespace std;//使用标准名字空间

inline int gi()//快速读入
{
	int f = 1, x = 0; char c = getchar();
	while (c < '0' || c > '9') { if (c == '-')f = -1; c = getchar();}
	while (c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar();}
	return f * x;
}

int a[50005], b[50005], add[50005], len, n, m;//a[],b[],add[]的意思如分析,len为每一块的长度,n为序列长度,m为询问个数,在本题中=n。

inline void modify(int l, int r, int x)//区间修改的自定义函数
{
	for (int i = l; i <= min(r, b[l] * len); i++) a[i] = a[i] + x;//增加序列中的数
	if (b[l] != b[r])//如果要修改的不在同一个块中
	{
		for (int i = (b[r] - 1) * len + 1; i <= r; i++) a[i] = a[i] + x;//继续增加序列中的数
	}
	for (int i = b[l] + 1; i <= b[r] - 1; i++) add[i] = add[i] + x;//给区间内的数增加标记
}

int main()//进入主函数
{
	n = gi();//输入元素个数
	len = sqrt(n);//求出每个块的长度
	for (int i = 1; i <= n; i++) a[i] = gi();//输入序列中的数
	for (int i = 1; i <= n; i++) b[i] = (i - 1) / len + 1;//求出序列中的数分别属于哪一个块
    for (int p = 1; p <= n; p++)
	{
		int fl = gi(), l = gi(), r = gi(), w = gi();//输入操作的描述
		if (!fl)//如果是修改
		{
			modify(l, r, w);//修改区间内的数
		}
		else//否则就是求出某个数
		{
			printf("%d\n", a[r] + add[b[r]]);//输出这个位置的数的标记和它在序列中原本的数的和
		}
	}
	return 0;//完美结束
}
posted @ 2019-06-28 21:52  csxsi  阅读(182)  评论(0编辑  收藏  举报