题解:洛谷 AT_abc465_c Reverse Permutation
【题目来源】
洛谷:AT_abc465_c [ABC465C] Reverse Permutation
【题目描述】
给定一个整数 \(N\) 和一个长度为 \(N\) 的字符串 \(S\),字符串 \(S\) 仅包含 o 和 x。
有一个长度为 \(N\) 的整数序列 \(A=(A_1,A_2,\ldots,A_N)\)。最初,\(A=(1,2,\ldots,N)\)。
你需要对 \(A\) 依次进行如下操作(从 \(k=1\) 到 \(k=N\)):
- 如果 \(S_k=\)
o,则反转 \(A\) 的前 \(k\) 个元素。即,将 \(A\) 变为 \((A_k,A_{k-1},\ldots,A_1,A_{k+1},A_{k+2},\ldots,A_N)\)。 - 如果 \(S_k=\)
x,则什么也不做。
请输出所有操作结束后 \(A\) 的最终状态。
【输入】
输入通过标准输入给出,格式如下:
\(N\)
\(S\)
【输出】
输出所有操作完成后 \(A\) 的元素,空格分隔。
【输入样例】
5
ooxoo
【输出样例】
5 2 1 3 4
【核心思想】
-
问题分析:给定长度为 \(N\) 的序列 \(A=(1,2,\ldots,N)\) 和操作字符串 \(S\)(
o表示反转前 \(k\) 个元素,x表示不操作),按 \(k=1\) 到 \(N\) 依次执行操作后输出最终序列。直接模拟每次反转前 \(k\) 个元素的时间复杂度为 \(O(N^2)\),需要优化。这是一个双端队列 + 奇偶性追踪问题,核心在于利用"反转的叠加效应"将 \(O(N^2)\) 的反转操作优化为 \(O(1)\) 的双端队列插入。 -
算法选择:
- 双端队列(Deque)+ 翻转次数奇偶性:用
flip_cnt记录累计反转次数。反转偶数次等价于未反转,反转奇数次等价于反转一次。利用这一性质,在奇数反转次数时从队首插入(模拟反转后的尾部),偶数时从队尾插入
- 双端队列(Deque)+ 翻转次数奇偶性:用
-
关键步骤:
- 读入数据:读取 \(N\) 和字符串 \(S\)
- 初始化:双端队列
dq为空,flip_cnt = 0 - 遍历处理(\(i\) 从 \(0\) 到 \(N-1\)):
- 若
flip_cnt % 2 == 0(当前方向正常):dq.push_back(i + 1) - 否则(当前方向已反转):
dq.push_front(i + 1) - 若 $S_i = $
o:flip_cnt++
- 若
- 最终调整:若
flip_cnt % 2 == 1,reverse(dq)(奇数次反转需要最终翻转) - 输出结果:依次输出
dq中元素
-
时间/空间复杂度:
- 时间复杂度:\(O(N)\),双端队列头尾插入均为 \(O(1)\),最终可能一次 \(O(N)\) 翻转
- 空间复杂度:\(O(N)\),双端队列存储 \(N\) 个元素
-
双端队列与反转奇偶性的核心思想:
- 反转的叠加等价性:连续两次反转前 \(k\) 个元素会恢复原状,因此只需关心反转次数的奇偶性,而非具体次数。
flip_cnt % 2足以判断当前序列方向 - 双端插入模拟反转:当序列处于"逻辑反转"状态(
flip_cnt为奇数)时,新元素 \(i+1\) 应被放到当前序列的"末尾"。但由于序列逻辑上已反转,物理上的队首对应逻辑上的队尾,因此用push_front实现 \(O(1)\) 的"逻辑尾部插入" - 惰性翻转策略:不立即执行物理反转,而是通过插入位置的选择延迟反转操作,只在最后根据总奇偶性决定是否执行一次物理翻转
- 操作顺序的精确对应:第 \(k\) 步操作前,前 \(k-1\) 步的反转效果已通过
flip_cnt的奇偶性体现在插入位置选择上,确保每一步都在正确的逻辑状态下执行 - 适用于序列反转优化、状态叠加类问题,核心是将频繁的前缀反转转化为 \(O(1)\) 的双端操作
- 反转的叠加等价性:连续两次反转前 \(k\) 个元素会恢复原状,因此只需关心反转次数的奇偶性,而非具体次数。
【算法标签】
普及- #队列
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int n; // n: 序列长度
string s; // s: 操作字符串,'o' 表示反转,'x' 表示不操作
deque<int> dq; // dq: 双端队列,用于高效地在两端插入元素
int flip_cnt; // flip_cnt: 记录 'o' 操作的次数(即需要反转的次数)
int main()
{
cin >> n >> s; // 读入序列长度和操作字符串
for (int i = 0; i < n; i++) // 遍历每个位置
{
if (flip_cnt % 2 == 0) // 如果当前反转次数为偶数,序列方向正常
dq.push_back(i + 1); // 从尾部加入元素
else // 如果当前反转次数为奇数,序列方向已反转
dq.push_front(i + 1); // 从头部加入元素(等效于在反转后的尾部加入)
if (s[i] == 'o') // 如果当前操作是 'o'
flip_cnt++; // 反转次数加一
}
if (flip_cnt % 2) // 如果最终反转次数为奇数,整体需要再反转一次
reverse(dq.begin(), dq.end());
for (int i = 0; i < dq.size(); i++) // 输出最终序列
cout << dq[i] << " ";
cout << endl;
return 0;
}
【运行结果】
5
ooxoo
5 2 1 3 4
浙公网安备 33010602011771号