题解:一个简单的整数问题
一、题目分析
本题给定一个长度为(N)的数列,以及(M)条指令,指令分为两种:一是将数列中指定区间的数都加上一个值;二是查询数列中指定区间的数的和。需要根据指令输出相应的查询结果。
二、解题思路
本题采用分块算法来解决。将数列分成若干块,通过维护每块的和以及块的增量信息,实现对区间操作和查询的优化。
(一)数据结构与变量定义
typedef long long LL;
const int N = 100010, M = 350;
int n, m, len;
LL add[M], sum[M];
int w[N];
LL
:定义长整型别名,用于处理较大的数值。N
:定义数列的最大长度。M
:定义分块后块的最大数量(这里是估算值,实际与数列长度有关)。n
:存储数列的实际长度。m
:存储指令的数量。len
:存储每块的长度。add[M]
:数组,用于存储每块的增量(即该块所有数统一增加的值)。sum[M]
:数组,用于存储每块的元素和。w[N]
:数组,用于存储原始数列。
(二)辅助函数
- 获取元素所属块的编号函数
get
:
int get(int i)
{
return i / len;
}
根据元素的下标i
,返回其所属块的编号。
(三)区间修改函数change
void change(int l, int r, int d)
{
if (get(l) == get(r)) // 段内直接暴力
{
for (int i = l; i <= r; i ++ ) w[i] += d, sum[get(i)] += d;
}
else
{
int i = l, j = r;
while (get(i) == get(l)) w[i] += d, sum[get(i)] += d, i ++ ;
while (get(j) == get(r)) w[j] += d, sum[get(j)] += d, j -- ;
for (int k = get(i); k <= get(j); k ++ ) sum[k] += len * d, add[k] += d;
}
}
- 如果区间
[l, r]
完全在同一块内,直接遍历该区间内的元素,将每个元素加上d
,并更新该块的和sum
。 - 如果区间跨越多个块,先处理区间两端不完整的块,遍历这两个块内属于区间
[l, r]
的元素并更新;然后处理中间完整的块,将这些块的和sum
增加len * d
(len
为块的长度),并记录块的增量add
。
(四)区间查询函数query
LL query(int l, int r)
{
LL res = 0;
if (get(l) == get(r)) // 段内直接暴力
{
for (int i = l; i <= r; i ++ ) res += w[i] + add[get(i)];
}
else
{
int i = l, j = r;
while (get(i) == get(l)) res += w[i] + add[get(i)], i ++ ;
while (get(j) == get(r)) res += w[j] + add[get(j)], j -- ;
for (int k = get(i); k <= get(j); k ++ ) res += sum[k];
}
return res;
}
- 如果查询区间
[l, r]
完全在同一块内,遍历该区间内的元素,将每个元素的值加上所属块的增量add
,累加到结果res
中。 - 如果查询区间跨越多个块,先处理区间两端不完整的块,遍历这两个块内属于区间
[l, r]
的元素,将元素值加上所属块的增量add
后累加到res
中;然后处理中间完整的块,直接将这些块的和sum
累加到res
中。
(五)主函数main
int main()
{
scanf("%d%d", &n, &m);
len = sqrt(n);
for (int i = 1; i <= n; i ++ )
{
scanf("%d", &w[i]);
sum[get(i)] += w[i];
}
char op[2];
int l, r, d;
while (m -- )
{
scanf("%s%d%d", op, &l, &r);
if (*op == 'C')
{
scanf("%d", &d);
change(l, r, d);
}
else printf("%lld\n", query(l, r));
}
return 0;
}
- 读取数列长度
n
和指令数量m
,计算每块的长度len
(取数列长度的平方根)。 - 读取原始数列,将每个元素加入到其所属块的和
sum
中。 - 循环处理每条指令:
- 如果指令为
C
(修改操作),读取区间和增加值d
,调用change
函数进行区间修改。 - 如果指令为
Q
(查询操作),调用query
函数查询区间和并输出结果。
- 如果指令为
四、时间复杂度和空间复杂度
(一)时间复杂度
- 初始化操作:读取数列并计算每块的和,时间复杂度为(O(n))。
- 修改操作:同一块内修改时间复杂度为(O(\sqrt{n}));跨块修改时,两端不完整块的处理和中间完整块的处理,时间复杂度也为(O(\sqrt{n}))。所以每次修改操作的时间复杂度为(O(\sqrt{n}))。
- 查询操作:同一块内查询时间复杂度为(O(\sqrt{n}));跨块查询时,时间复杂度同样为(O(\sqrt{n}))。
- 总的时间复杂度为(O(M\sqrt{n})),其中
M
是指令的数量。
(二)空间复杂度
需要存储数列w[N]
、每块的和sum[M]
以及每块的增量add[M]
,空间复杂度为(O(N + \sqrt{N})),近似为(O(N))。