紫书上的链表部分是基于数组的,不同于数据结构书上使用基于指针的链表。这样一来代码读起来就会有点晦涩难懂。这题也不例外,这里主要是练习使用 双向链表 。
先看看这两个数组:
const int maxn = 100000 + 5;
int n, left[maxn], right[maxn];
left数组中保存着每个盒子的左边的盒子的编号,right也就是保存着每个盒子右边的盒子编号。
然后看看这个link()函数吧:
inline void link(int L, int R) {
right[L] = R; left[R] = L;
}
这个函数的主要作用要明确,就是接收两个盒子的编号(L,R),并将其连接起来。怎么连接呢?函数中写的十分清楚。将L的右边设置为R,将R的左边设置为L。即 … – L – R – … 。这样一来不就连起来了吗。
再来看看main()函数:
int main() {
int m, kase = 0;
while(scanf("%d%d", &n, &m) == 2) {
for(int i = 1; i <= n; i++) {
left[i] = i-1;
right[i] = (i+1) % (n+1); //这里主要是为了将right[n] = 0;
}
right[0] = 1; left[0] = n;
int op, X, Y, inv = 0;
while(m--) {
scanf("%d", &op);
if(op == 4) inv = !inv; //这里是本题的一个技巧。inv为1表示反链表,为0表示原链表
else {
scanf("%d%d", &X, &Y);
if(op == 3 && right[Y] == X) swap(X, Y); //这也是一个技巧,这里在下面会体现出
if(op != 3 && inv) op = 3 - op; //如果是反链表,就将操作1和操作2交换。
if(op == 1 && X == left[Y]) continue;
if(op == 2 && X == right[Y]) continue;
int LX = left[X], RX = right[X], LY = left[Y], RY = right[Y];
if(op == 1) {
link(LX, RX); link(LY, X); link(X, Y);
}
else if(op == 2) {
link(LX, RX); link(Y, X); link(X, RY);
}
else if(op == 3) {
//这里就体现swap(X, Y)的作用了,当编号为X和Y的两个箱子相邻时我们需要特殊处理。
if(right[X] == Y) { 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 % 2 == 1) ans += b;
}
if(inv && n % 2 == 0) ans = (long long)n*(n+1)/2 - ans;
printf("Case %d: %lld\n", ++kase, ans);
}
return 0;
}