你有一行盒子,从左到右依次编号为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 2 3 4 5 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,以下m行每行包含一条指令。
【输出格式】
每组数据输出一行,即所有奇数位置的盒子编号之和。位置从左到右编号为1~n。
【输入样例】
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
【输出样例】
12
9
2500050000
【数据范围】
N,M<=100000
双向链表记录每一个数的前驱和后继
唯一麻烦的是操作4
用一个变量标记是否翻转
不难想到翻转后把A移到B前等价于把A移到B之后再翻转
最后计算奇项和的时候:
1.有奇数个数,则翻转对计算无影响
2.若有偶数个数,则翻转后的奇项和等于数列总和减去翻转前的奇项和
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cctype>
#include<vector>
#include<queue>
#define maxn 100005
using namespace std;
int n,m,ch,x,y;
int next[maxn],last[maxn];
void link(int a,int b)
{
next[a]=b;
last[b]=a;
}
int main()
{
//freopen("in.txt","r",stdin);
while(scanf("%d%d",&n,&m)==2)
{
long long all=0,ans=0;
int ok=0;
next[0]=1;
for(int i=1;i<=n;i++)
{
next[i]=i+1;
last[i]=i-1;
}
next[n]=0;
for(int i=1;i<=m;i++)
{
scanf("%d",&ch);
if(ch==4) ok=!ok;
else
{
scanf("%d%d",&x,&y);
if(ch==3&&next[y]==x) swap(x,y);
if(ok&&ch<3) ch=3-ch;
int ly=last[y],lx=last[x],nx=next[x],ny=next[y];
if(ch==1) //x移到y的左边
{
if(ly==x) continue;
link(lx,nx);
link(x,y);
link(ly,x);
}
if(ch==2) //x移到y的右边
{
if(ny==x) continue;
link(lx,nx);
link(y,x);
link(x,ny);
}
if(ch==3)
{
if(nx==y) //若x,y相邻
{
link(lx,y);
link(y,x);
link(x,ny);
}
else
{
link(ly,x);
link(lx,y);
link(x,ny);
link(y,nx);
}
}
}
}
int k=0;
for(int i=1;i<=n;i++)
{
k=next[k];
all+=k;
if(i%2) ans+=k;
}
if(n%2==0&&ok==1) ans=all-ans;
cout<<ans<<endl;
}
return 0;
}