题意:移动盒子,有4种操作:
1 X Y表示把盒子X移动到盒子Y左边(如果X已经在Y的左边则忽略此指令)。
2 X Y表示把盒子X移动到盒子Y右边(如果X已经在Y的右边则忽略此指令)。
3 X Y表示交换盒子X和Y的位置。
4 表示反转整条链。
最后计算所有奇数位置的盒子编号之和。
思路:利用双向链表,Left[i]代表编号i盒子左边的盒子编号,Right[i]代表编号i盒子右边盒子的编号。
当操作1时:X移到Y的左边:
例如:1 2 3 4 5 6
将1移到4的左边为:2 3 1 4 5 6
此时发生变化的有(1)1的左边和右边 ,(2)2的左边和右边 ,(3)3 的右边 ,(4)4的左边
俩个结点相互连接:void Link(int L,int R){Right[L] = R ;Left[R] = L;};
起初的Left[] = {6,0,1,2,3,4,5 } , Right[] = {1,2,3,4,5,6,0}
x = 1,y = 4
lx = Left[x],rx = Right[x],ly = Left[y],ry = Right[y];
所以有:Link(lx,rx);--->0原本右边是1,而1要移走,所以0的右边改为2;2原本左边是1,而1要移走,所以2 的左边改为0。
Link(ly,x); --->3原本右边是4,而1将插入,所有3的右边改为1;1原本左边是0,而1新插入,左边是3,所以,1的左边改为3。
Link(x,y); --->1原本的右边是2,而1新插入位置后,右边为4,所以该为4;而4原本的左边为3,而当前插入了1,所以4的左边改为1。
其他同理即可!
参考:入门经典--例题6-5--P145
代码:
#include <iostream>
#include <cstdio>
#define MAXN 100005
using namespace std;
int n,Left[MAXN],Right[MAXN];
void link(int L,int R)
{
Right[L] = R;
Left[R] = L;
}
int main()
{
int m,kase = 0;
while(scanf("%d%d",&n,&m)!=EOF)
{
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)//将X移到Y的左边
{
link(lx,rx);//X要发生改变,所以X的左值的后一个和右值的前一个也会发生变化,左值的右值为x的右值,右值的左值为x的左值
link(ly,x);//同理,Y的左值的右值发生变化,为新插入的X值,当前X的左值也变为Y的左值
link(x,y);//同理,新插入的X的右值为Y,Y的左值改变为X!
}
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)
ans += b;
}
if(inv && n % 2 == 0)//当翻转时是将总个数减去计算出的奇数和
ans = (long long)n*(n+1)/2 - ans;
printf("Case %d: %lld\n",++kase,ans);
}
return 0;
}