题解:
lrj大法。
采用双向链表,编号为 i 的的盒子左右两边的盒子编号分别为 left[i] 和 right[i] 。
技巧:操作4反转需要修改所有元素的指针,为避免时间消耗,增加一个标记 inv, 表示有无反转,这样就并不需要真的去执行操作4.
inv = 1表示奇数次反转,inv = 0表示偶数次反转。当inv = 1的时候,操作1和2需要反过来,操作3不受inv取值的影响。
注意操作3,相邻的两个结点和不相邻的两个结点的交换,其连接方式是不同的。
//running time: 0.093s
#include <iostream>
#include <cstdio>
#include <algorithm>
const int maxn = 100000 + 10;
int n, m;
int left[maxn], right[maxn];
void inline link(int l, int r) //两个结点相互连接,l在r的左边,r在l的右边
{
right[l] = r, left[r] = l;
}
int main()
{
#ifdef LOCAL
freopen("data.in", "r", stdin);
#endif // LOCAL
int kcase = 0;
while(std::cin >> n >> m)
{
//初始化
for(int i = 1; i <= n; ++i)
{
left[i] = i - 1;
right[i] = i + 1;
}
right[0] = 1, left[0] = n, right[n] = 0;
int op, x, y, inv = 0;
for(int i = 0; i < m; ++i)
{
scanf("%d", &op);
if(op == 4){
inv = !inv;
continue;
}
scanf("%d %d", &x, &y);
if(op == 3 && x == right[y]) std::swap(x, y);
if(op != 3 && inv) op = 3 - op;
if((op == 1 && x == left[y]) || (op == 2 && x == right[y])) continue;
int lx = left[x], rx = right[x], ly = left[y], ry = right[y];
//move x to the left of y, actually the link relation of x and y modified
if(op == 1){
link(lx, rx); link(ly, x); link(x, y);
}
//move x to the right of y
else if(op == 2){
link(lx, rx); link(y, x); link(x, ry);
}
//swap x and y
else if(op == 3){
if(y == right[x]) { link(lx, y); link(y, x); link(x, ry); }
else{ link(lx, y); link(y, rx); link(ly, x); link(x, ry); }
}
}
int b = 0;
long long ans = 0;
for(int i = 1; i <= n; ++i){
b = right[b];
if(i & 1) ans += b;
}
if(inv && n % 2 == 0) ans = (long long)n * (n + 1) / 2 - ans;
printf("Case %d: %lld\n", ++kcase, ans);
}
return 0;
}