题目大意

你有一行盒子,从左到右依次编号为1, 2, 3,…, n。你可以执行四种指令:

1 X Y表示把盒子X移动到盒子Y左边(如果X已经在Y的左边则忽略此指令)。
2 X Y表示把盒子X移动到盒子Y右边(如果X已经在Y的右边则忽略此指令)。
3 X Y表示交换盒子X和Y的位置。
4 表示反转整条链。

盒子个数n和指令条数m(1<=n,m<=100,000)

题解

用数组来模拟链表操作,对于每个节点设置一个前驱和后继。

1操作是把x的前驱节点和x的后继节点连接,y节点的前驱和x节点连接,x节点和y节点连接。

2,3,的做法和1差不多

4操作由于操作两次就等于没有操作,所以只要判断它最终是不是执行了奇数次,如果是就把n个节点的前驱和后继交换下。还有就是在执行1,2的时候如果之前4操作了奇数次,那么1,2两个执行的操作分别是2操作和1操作。

代码:

 #include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <utility>
#include <vector>
#include <queue>
using namespace std;
#define INF 0x3f3f3f3f
#define maxn 111111
typedef long long LL;
int nxt[maxn], pre[maxn];
void init(int n)
{
for (int i = ; i <= n;i++)
{
pre[i] = i - ;
nxt[i] = i + ;
}
nxt[n] = ;
nxt[] = ; pre[] = n;
}
void link(int l, int r)
{
pre[r] = l; nxt[l] = r;
}
int main()
{
int n, m,kase=;
while (scanf("%d%d", &n, &m) != EOF)
{
int rev = ;
init(n);
while (m--)
{
int op, x, y;
scanf("%d", &op);
if (op == ) rev = - rev;
else
{
scanf("%d%d", &x, &y);
if ((op == || op == ) && rev) op = - op;
if (op == &&nxt[y] == x) swap(x, y);
int lx, rx, ly, ry;
lx = pre[x]; rx = nxt[x];
ly = pre[y]; ry = nxt[y];
if (op == )
{
if (nxt[x]==y) continue;
link(lx, rx);
link(ly, x);
link(x, y); }
else if (op == )
{
if (nxt[y]==x) continue;
link(lx, rx);
link(x, ry);
link(y, x);
}
else
{
if (nxt[x] == y)
{
link(lx, y);
link(y, x);
link(x, ry);
}
else
{
link(lx, y);
link(y, rx);
link(ly, x);
link(x, ry);
}
}
}
}
if (rev)
{
for (int i = ; i <= n; i++) swap(pre[i], nxt[i]);
}
int pos;
for (int i = ; i <= n; i++)
{
if (pre[i] == )
{
pos = i;
break;
}
}
int cnt = ;
LL ans = ;
while (pos!=)
{
if (cnt & ) ans += pos;
pos = nxt[pos];
cnt++;
}
printf("Case %d: %I64d\n", ++kase,ans);
}
return ;
}
04-17 03:01