题意:你有一行盒子,从左到右依次编号为1,2,3...n。可执行以下4种指令:
□1 X Y表示把盒子X移动到盒子Y的左边(如果X已经在Y的左边则忽略此指令)。
□2 X Y表示把盒子X移动到盒子Y的右边(如果X已经在Y的右边则忽略此指令)。
□3 X Y表示交换盒子X和Y的位置。
□4表示反转整条链。
指令保证合法,即X不等于Y。例如,当n=6时在初始状态下执行1 1 4后,盒子序列为2 3 1 4 5 6。接下来执行2 3 5,盒子序列变为2 1 4 5 3 6。在执行3 1 6,得到2 6 4 5 3 1。最终执行4,得到1 3 5 4 6 2。
输入包含不超过10组数据,每组数据第一行为盒子个数n和指令条数m(1<=n,m<=100000),以下m行每行包含一条指令。每组数据输出一行,即所有奇数位置的盒子编号之和。位置从左到右编号为1~n。
【分析】
根据前面的经验,如果用数组来保存盒子,肯定会超时,但如果像例题6-4那样只保存个next值。似乎又不够,怎么办?解决方法是采用双向链表(doubly linked list) :用left[i]和right[i]分别表示编号为i的盒子左边和右边的盒子编号(如果是0,表示不存在),则下面的过程可以让两个结点相互连接 void link(int L,int R){
right[L] = R: left[R] = L;
}
提示6-5: 在双向链表这样 的复杂链式结构中,往往会编写一些辅助函数用来设置链接关系。
有了这个代码,可以先记录好操作之前X和Y两边的结点,然后用link函数按照某种顺序把它们连起来。操作4 比较特殊,为了避免一次修改所有元素的指针,此处增加一个记inv,表示有没有执行过操作4(如果inv=l 时再执行一次操作4,则inv变为0)。这样,当p为1和2且inv=1时,只需把OP变成3-op(注意操作3不受inv影响) 即可。最终验出时要根据inv的值进行不同处理。
提示6-6: 如果数据结构上的某一个操作很耗时,有时可以用加标记的方式处理,而不需要真的执行那个操作。但同时,该数据结构的所有其他操作都要考虑这个标记。
有了这个代码,可以先记录好操作之前X和Y两边的结点,然后用link函数按照某种顺序把它们连起来。操作4 比较特殊,为了避免一次修改所有元素的指针,此处增加一个记inv,表示有没有执行过操作4(如果inv=l 时再执行一次操作4,则inv变为0)。这样,当p为1和2且inv=1时,只需把OP变成3-op(注意操作3不受inv影响) 即可。最终验出时要根据inv的值进行不同处理。
提示6-6: 如果数据结构上的某一个操作很耗时,有时可以用加标记的方式处理,而不需要真的执行那个操作。但同时,该数据结构的所有其他操作都要考虑这个标记。
下面的核心代码里还有一些可以借鉴的细节处理,请读者仔细阅读:
#include<bits/stdc++.h> using namespace std; const int MAXN = 100005; int n, m, Left[MAXN], Right[MAXN]; void link(int L, int R) { Right[L] = R; Left[R] = L; } int main() { int CASE = 1; while (~scanf("%d%d", &n, &m)) { for (int i = 0; i <= n+1; i++) Left[i] = i-1, Right[i] = i+1; int op, X, Y; bool inv = false;//是否反转 while (m--) { scanf("%d", &op); if (op == 4) inv = !inv; else { scanf("%d%d", &X, &Y); if (inv && op != 3) op = 3 - op;//如果链表反转了,交换1,2操作 if (op == 3 && Right[Y] == X) swap(X, Y);//对于3操作中XY和YX这种情况->XY int LX = Left[X], RX = Right[X], LY = Left[Y], RY = Right[Y]; if (op == 1 && X != Left[Y]) { link(LX, RX); link(LY, X); link(X, Y); } if (op == 2 && X != Right[Y]) { link(LX, RX); link(Y, X); link(X, RY); } if (op == 3) { 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) ans += b; } if (inv && n%2 == 0) ans = (long long)n*(n+1)/2 - ans; printf("Case %d: %lld\n", CASE++, ans); } return 0; }