原题如下:UVA-12657
You have n boxes in a line on the table numbered 1 . . . n from left to right. Your task is to simulate 4
kinds of commands:
• 1 X Y : move box X to the left to Y (ignore this if X is already the left of Y )
• 2 X Y : move box X to the right to Y (ignore this if X is already the right of Y )
• 3 X Y : swap box X and Y
• 4: reverse the whole line.
Commands are guaranteed to be valid, i.e. X will be not equal to Y .
For example, if n = 6, after executing 1 1 4, the line becomes 2 3 1 4 5 6. Then after executing
2 3 5, the line becomes 2 1 4 5 3 6. Then after executing 3 1 6, the line becomes 2 6 4 5 3 1.
Then after executing 4, then line becomes 1 3 5 4 6 2
Input
There will be at most 10 test cases. Each test case begins with a line containing 2 integers n, m
(1 ≤ n, m ≤ 100, 000). Each of the following m lines contain a command.
Output
For each test case, print the sum of numbers at odd-indexed positions. Positions are numbered 1 to n
from left to right.
Sample Input
6 4
1 1 4
2 3 5
3 1 6
4
6 3
1 1 4
2 3 5
3 1 6
100000 1
4
Sample Output
Case 1: 12
Case 2: 9
Case 3: 2500050000
分析
这个题我按照我自己的习惯用指针建立的双向链表来储存数据,感觉比用数组的链表结构更加容易理解。题目涉及到四种操作:
- 1 X Y :把X放在Y的左边
首先p,n相连,然后让p指向y的pre,再让p,x,y相连即可
-
2 X Y:把X放在Y的右边
道理同上 -
3 X Y:交换X,Y的位置
这里需要重点讨论下,因为X,Y是有可能相邻 的
- 3.1 X,Y不相邻
这种情况还比较好讨论,只需要记录X的前驱和后继,以及Y的前驱和后继,再分别连接对应结点即可 - 3.2 X,Y相邻
相邻有两种情况,要加以区分,具体过程参见代码
- 4 反序
我这里开始没看紫书,交上去后wa了,后来才发现,在反序之后,元素的左右相对位置发生了改变,所以操作1变成了操作2。所以在判断操作情况的时候,得加入一段语句:
if (cmd != 3 && flag)cmd = 3 - cmd;
当然,反序之后输出就应当是从末尾开始输出,但实际上链表的总体结构并未改变。这里紫书上最后用了很巧妙的一个判断:
if (flag && !(N % 2))ans = (long long)N * (N + 1) / 2 - ans;
仔细想想也是,即使反序,如果N为奇数,那么输出的总和还是不变;只有当反序且N为偶数的时候,输出才为整体的总和减去之前累计的和。
下面是整体代码:
#include <cstdio>
#include <cstring>
using namespace std;
struct Node {
int data;
Node* pre;
Node* next;
};
const int maxn = 100000 + 5;
Node node[maxn];
void cnnt(Node* a, Node* b) {
a->next = b;
b->pre = a;
}
int main() {
int N, M, cnt = 0;
while (~scanf("%d%d", &N, &M)) {
for (int i = 1;i <= N;i++) {
node[i].data = i;
if (i > 1)node[i].pre = &node[i - 1];
if (i < N)node[i].next = &node[i + 1];
}
Node* head = new Node();
Node* tail = new Node();
head->next = &node[1];
node[1].pre = head;
tail->pre = &node[N];
node[N].next = tail;
/*Node* t = head->next;
while (t != tail) {
printf("%d", t->data);
t = t->next;
}
printf("\n");
t = tail->pre;
while (t != head) {
printf("%d", t->data);
t = t->pre;
}
printf("\n");*/
int flag = 0;
while (M--) {
int cmd, X, Y;
scanf("%d", &cmd);
if (cmd == 4) { flag = !flag;continue; }
if (cmd != 3 && flag)cmd = 3 - cmd;
scanf("%d%d", &X, &Y);
Node* x = &node[X];
Node* y = &node[Y];
Node* p = x->pre;
Node* n = x->next;
if (cmd == 1 && x->next != y) {
cnnt(p, n);
p = y->pre;
cnnt(p, x);
cnnt(x, y);
}
else if (cmd == 2 && y->next != x) {
cnnt(p, n);
n = y->next;
cnnt(y, x);
cnnt(x, n);
}
else if (cmd == 3 && x->next != y && y->next != x) {
Node* pp = y->pre;
Node* nn = y->next;
cnnt(pp, x);
cnnt(x, nn);
cnnt(p, y);
cnnt(y, n);
}
else if (cmd == 3 && x->next == y) {
cnnt(x, y->next);
cnnt(y, x);
cnnt(p, y);
}
else if (cmd == 3 && y->next == x) {
cnnt(y->pre, x);
cnnt(x, y);
cnnt(y, n);
}
}
long long ans = 0;
int p = 1;
Node* t = head->next;
while (t != tail) {
if (p) { ans += t->data; }
t = t->next;
p = !p;
}
if (flag && !(N % 2))ans = (long long)N * (N + 1) / 2 - ans;
printf("Case %d: %lld\n", ++cnt, ans);
}
return 0;
}
再贴上紫书上的源码供学习:
// UVa12657 Boxes in a Line
// Rujia Liu
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 100000 + 5;
int n, left[maxn], right[maxn];
inline void link(int L, int R) {
right[L] = R; left[R] = L;
}
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[0] = 1; left[0] = n;
int op, X, Y, inv = 0;
while(m--) {
scanf("%d", &op);
if(op == 4) inv = !inv;
else {
scanf("%d%d", &X, &Y);
if(op == 3 && right[Y] == X) swap(X, Y);
if(op != 3 && inv) op = 3 - op;
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) {
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;
}