题解:洛谷 AT_abc425_c Rotate and Sum Query
【题目来源】
洛谷:AT_abc425_c [ABC425C] Rotate and Sum Query - 洛谷
【题目描述】
给你一个长度为 \(N\) 的整数序列 \(A=(A_1,A_2,\ldots,A_N)\)。
现在需要按顺序处理 \(Q\) 个查询。查询有两种类型,格式如下:
1 c:将序列 \(A\) 的第一个元素移动到序列末尾,重复 \(c\) 次。2 l r:输出 \(\displaystyle \sum_{i=l}^r A_i\) 的值。
【输入】
输入从标准输入给出,格式如下:
\(N\) \(Q\) \(A_1\) \(A_2\) \(\ldots\) \(A_N\) \(\text{query}_1\) \(\text{query}_2\)
\(\vdots\)
\(\text{query}_Q\)
每个查询 \(\text{query}_i\) 有如下两种格式之一:
\(1\) \(c\)
\(2\) \(l\) \(r\)
【输出】
对于每个第二种类型的查询,按照出现顺序输出查询结果,每个结果占一行。
【输入样例】
4 3
3 1 4 5
2 1 3
1 1
2 2 3
【输出样例】
8
9
【核心思想】
-
问题分析:给定长度为 \(N\) 的序列 \(A\),支持两种操作:将首元素移到末尾 \(c\) 次(循环左移),以及查询区间 \([l, r]\) 的和。这是一个循环数组 + 前缀和问题,关键在于将循环移位转化为偏移量,利用数组复制技巧将循环结构线性化,从而用前缀和 \(O(1)\) 回答区间查询。
-
算法选择:
- 数组复制线性化:将原数组 \(A[1..N]\) 复制一份接在末尾,形成 \(A[1..2N]\),使任意循环区间都能映射为连续子数组
- 偏移量维护:用
cnt记录累计循环左移次数,实际查询位置为 \(l + cnt\) 和 \(r + cnt\) - 前缀和查询:对扩展后的数组建前缀和 \(sa\),区间和 = \(sa[r] - sa[l-1]\)
-
关键步骤:
- 读取输入:读入 \(N, Q\) 和数组 \(A[1..N]\)
- 数组扩展:\(A[i+N] = A[i]\)(\(i\) 从 \(1\) 到 \(N\)),实现循环数组的线性表示
- 前缀和初始化:\(sa[i] = sa[i-1] + A[i]\)(\(i\) 从 \(1\) 到 \(2N\))
- 处理查询:
- 类型 \(1\ c\):
cnt = (cnt + c) % N,更新循环偏移量 - 类型 \(2\ l\ r\):实际区间变为 \([l+cnt,\ r+cnt]\),答案 = \(sa[r+cnt] - sa[l+cnt-1]\)
- 类型 \(1\ c\):
- 输出答案
-
时间/空间复杂度:
- 时间复杂度:\(O(N + Q)\),预处理 \(O(N)\),每次查询 \(O(1)\)
- 空间复杂度:\(O(N)\),扩展数组和前缀和各需 \(2N\) 空间
-
循环数组线性化的核心思想:
- 空间换时间:通过复制数组将循环结构"拉直",使任意循环区间都能表示为普通连续区间
- 偏移量抽象:将多次循环移位抽象为单个偏移量
cnt,避免每次实际移动元素 - 模运算保界:
cnt % N保证偏移量始终在 \([0, N-1]\) 范围内,防止数组越界 - 适用于"循环移位 + 区间查询"类问题,核心是维护虚拟偏移而非物理移动
【算法标签】
普及- #前缀和
【代码详解】
#include <bits/stdc++.h>
using namespace std;
#define int long long // 使用长整型防止数据溢出
const int N = 200005 * 2; // 定义数组最大长度(扩展为两倍)
int n; // 原始数组长度
int q; // 查询次数
int cnt; // 循环偏移计数器
int a[N]; // 原始数组(扩展为两倍实现循环)
int sa[N]; // 前缀和数组
signed main()
{
// 输入数组长度和查询次数
cin >> n >> q;
// 输入原始数组并扩展为两倍长度(实现循环数组)
for (int i = 1; i <= n; i++)
{
cin >> a[i];
a[i + n] = a[i]; // 复制一份实现循环数组
}
// 计算前缀和数组(sa[i]表示前i个元素的和)
for (int i = 1; i <= 2 * n; i++)
{
sa[i] = sa[i - 1] + a[i];
}
// 处理每个查询
while (q--)
{
int op, c, l, r;
cin >> op; // 查询类型(1或2)
if (op == 1) // 类型1:循环偏移操作
{
cin >> c;
cnt = (cnt + c) % n; // 更新偏移量(取模保证在数组范围内)
}
else // 类型2:区间求和查询
{
cin >> l >> r;
// 计算偏移后的实际位置
l = l + cnt;
r = r + cnt;
// 使用前缀和数组快速计算区间和
cout << sa[r] - sa[l - 1] << endl;
}
}
return 0;
}
【运行结果】
4 3
3 1 4 5
2 1 3
8
1 1
2 2 3
9
浙公网安备 33010602011771号