解题思路:
刚开始想的是直接用STL的list直接模拟,结果写了一发,很无奈,超时了,因此必须自己用数组模拟双向链表进行模拟,因此STL慎用,虽然好用方便但是对于有的题时间性能要求比较高,STL就成了瓶颈。。。
先给出一个连接两个节点的函数:
void link(int L, int R) {
right[L] = R; left[R] = L;
}
有了这个代码,可以先记录好操作之前X和Y两边的结点,然后用link函数按照某种顺序把它们连起来。操作4比较特殊,为了避免一次修改所有元素的指针,此处增加一个标记inv,inv=0表示没有执行过操作4(如果inv=1时再执行一次操作4,则inv变为0)。这样,当操作op为1和2且inv=1时,只需把op变成3-op(注意操作3不受inv影响)即可。最终输出时要根据inv的值进行不同处理。因为题目要求计算所有奇数位置的盒子编号之和,因此如果最终inv=1并且盒子总个数为奇数的话,那么反转对于本题的计算结果是没有影响的,因为以前位于奇数位置的现在还在奇数位置,只有当inv=1并且盒子总个数为偶数时计算结果才会与上面计算的ans不同,最终的ans=(LL)n*(n+1)/2-ans.
注意:如果ans定义的是long long,那么输出格式必须是%lld,不能使%I64d,否则会wa,别问我为什么,UVa上就是如此,我就因为这个wa了好多发,坑哭了,不信你试试!!!
贴代码:
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <ctype.h>
#include <iostream>
#include <map>
#include <list>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>
#define eps 1e-8
#define INF 0x7fffffff
#define PI acos(-1.0)
#define seed 31//131,1313
#define MAXV 50010
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
typedef long long LL;
typedef unsigned long long ULL;
using namespace std;
const int maxn = 100000 + 10;
int Right[maxn],Left[maxn];
void link(int L,int R){
Right[L]=R;
Left[R]=L;
}
int main(){
int n,m,cnt=1;
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])||(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;
LL ans=0;
for(int i=1;i<=n;i++){
b=Right[b];
if(i%2) ans+=b;
}
if(inv&&n%2==0){
ans=(LL)n*(n+1)/2-ans;
}
printf("Case %d: %lld\n",cnt++,ans);
}
}