题目链接:传送门
分析:每次操作都会花费大量时间,显然我们只需要关注每个元素的左边是啥,右边是啥就够了,那么用双向链表,l[i]表示i左边的数,r[i]表示i右边的数,每次操作模拟一下数组的变化就好了.
不过这样有一个问题:第4个操作似乎无法很高效地进行,如果真的按照它说的那样模拟翻转,恐怕复杂度和暴力也要差不多了,那么我们怎么处理呢?
观察发现题目只让我们输出奇数位置的编号和,也就是说,如果题目给定的序列长度就是奇数的,那么无论用多少次4操作都没有影响,否则4的次数是奇数,则用总和减去当前奇数位置的和就是答案。这就相当于线段树中的lazy标记的做法,现在要考虑这个标记对其它操作的影响,显然对3操作是没有任何影响的,而对1,2操作无非就是把1变成了2,把2变成了1,这样就很快地就能解决问题了。
如果数据结构上的某一个操作很耗时,有时可以用加标记的方式处理,而不需要真的执行那个操作。但同时,该数据结构的所有其他操作都要考虑这个标记!
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <queue> using namespace std; const int maxn = 100010; int n, m, kase, l[maxn], r[maxn],flag; long long ans; int main() { while (scanf("%d%d", &n, &m) == 2) { for (int i = 1; i <= n; i++) { l[i] = i - 1; r[i] = (i + 1) % (n + 1); } l[0] = n; r[0] = 1; flag = 0; for (int i = 1; i <= m; i++) { int op,x,y; scanf("%d", &op); if (op == 4) flag = (flag + 1) % 2; else { scanf("%d%d", &x, &y); if (op != 3 && flag) op = 3 - op; if (op == 1 && l[y] == x) continue; if (op == 2 && r[y] == x) continue; if (op == 1) { l[r[x]] = l[x]; r[l[x]] = r[x]; r[l[y]] = x; r[x] = y; l[x] = l[y]; l[y] = x; } else if (op == 2) { l[r[x]] = l[x]; r[l[x]] = r[x]; l[x] = y; r[x] = r[y]; l[r[y]] = x; r[y] = x; } else if (op == 3) { if (r[x] == y) { r[l[x]] = y; l[y] = l[x]; r[y] = x; l[x] = y; r[x] = r[y]; l[r[y]] = x; } else { r[l[y]] = x; l[r[y]] = x; r[l[x]] = y; l[r[x]] = y; swap(l[x], l[y]); swap(r[x], r[y]); } } } int cur = 0; ans = 0; for (int i = 1; i <= n; i++) { cur = r[cur]; if (i % 2 == 1) ans += cur; } if (flag && n % 2 == 0) ans = (long long)n * (n + 1) / 2 - ans; } printf("Case %d: %lld ", ++kase, ans); } return 0; }