【UVA-12657】解题报告(双端链表)

原始题目

题目大意

\(n\)个箱子,初始标号从左到右由\(1\)\(n\),有以下四种操作 1. 把X移动到Y左边 2. 把X移动到Y右边 3. 交换X和Y的位置 4. 全体翻转颠倒

其中1,2若已经符合状态则不操作

给出\(n\)和所有的操作,输出从左到右奇数位的和。

解题思路

  • 数组形式双端链表 \(left[i],right[i]\),模拟实现
  • 简化实现:3不受4影响,4的翻转可以记录状态,且翻转状态下2->1,1->2。
  • 交换需要分类,相邻时特判

解题代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#include <cstdio>
#include <iostream>
#include <iomanip>
#include <vector>
#include <string>
#include <cstring>
#define rep(i,a,n) for(int i=a;i<n;++i)
#define per(i,a,n) for(int i=n-1;i>=a;--i)
#define clint(x,a) memset(x,a,sizeof(int)*n)
#define clll(x,a) memset(x,a,sizeof(ll)*n)
#define pb push_back
#define np next_permutation
#define mp make_pair
#define fi first
#define se second
#define INF 0x3f3f3f3f
#define EPS 1e-8
#define PI acos(-1.0)
using namespace std;
const int maxn=1e5+5;
const int maxm=1e5+5;
const int maxl=26;

typedef long long ll;
typedef unsigned long long ull;
typedef vector<int> vi;
typedef pair<int,int> pii;
typedef pair<string,string> pss;
int a[maxn],lleft[maxn],rright[maxn];
int n,m;
void init(){
rep(i,1,n+1){
lleft[i]=i-1;
rright[i]=(i+1);
}
rright[0]=1;
lleft[n+1]=n;


}
void link(int l,int r){
rright[l]=r;lleft[r]=l;
}

int main(){
ios::sync_with_stdio(false);
int kase=0;
while(cin>>n>>m){
init();
int op,aa,bb,status=0;
rep(i,0,m){
cin>>op;
if(op==4){
status=!status;
}
else{
cin>>aa>>bb;
if(status && op!=3) op=3-op; //改变操作
if(op==3 && rright[bb]==aa) swap(aa,bb);
int laa=lleft[aa],raa=rright[aa],lbb=lleft[bb],rbb=rright[bb];
if(op==1){
if(aa==lbb) continue;
else {
link(laa,raa);
link(lbb,aa);
link(aa,bb);
}

}
else if(op==2){
if(aa==rbb) continue;
else {
link(laa,raa);
link(bb,aa);
link(aa,rbb);
}

}
else if(op==3){
if(raa==bb){
link(laa,bb);
link(bb,aa);
link(aa,rbb);
}
else{
link(laa,bb);
link(bb,raa);
link(lbb,aa);
link(aa,rbb);
}
}
}
}
ll sum=0;
int b;
if(status) b=n+1;
else b=0;
for(int i=1;i<=n;++i){
if(status){
b=lleft[b];
}
else b=rright[b];
if(i&1) sum+=b;
}
cout<<"Case "<<++kase<<": "<<sum<<endl;
// int c=0;
// for(int i=1;i<=n;++i){
// c=rright[c];
// cout<<c<<" ";
// }
// cout<<endl;
}
}

收获与反思

  • 数组形式双端链表\(left[i],right[i]\),分别表示前驱结点和后继结点的指针。

    //link操作,将两结点连接在一起。 void link(int l,int r){ rright[l]=r;lleft[r]=l; }

  • 对于1,2操作,需要考虑连接顺序,记录原始的lx,ly,rx,ry,然后从左到右一一link就好。

  • 注意记录原始的lx,ly,rx,ry,后面会改变