线性表

  1. 线性表就像是一条线,不会分叉。线性表有唯一的开始和结束,除了第1个元素,每个元素都有唯一的直接前驱;除了最后一个元素,每个元素都有唯一的直接后继
  2. 线性表有两种存储方式:顺序存储和链式存储。采用顺序存储的线性表被称为顺序表,采用链式存储的线性表被称为链表

顺序表

  1. 顺序表是顺序存储方式,即逻辑上相邻的数据在计算机内的存储位置也是相邻的。在顺序存储方式中,元素存储是连续的,中间不允许有空。
  2. 顺序表可以分为静态分配和动态分配两种。
  3. 用定长数组存储的方法被称为静态分配:
#define Maxsize 100//最大空间
typedef struct{
	int data[Maxsize];
	int length;//顺序表的长度
}SqList;//用typedef将结构体等价于类型名
  1. 动态分配结构体定义:
typedef struct{
	int *pr;//基地址,前面加*表示取地址中的内容
	int length;//顺序表的长度
}SqList;//用typedef将结构体等价于类型名

插入

步骤

  1. 判断插入位置线性DP 训练 codeforces gzcl线性训练计划_链表是否合法线性DP 训练 codeforces gzcl线性训练计划_线性DP 训练 codeforces_02,可以在第1个元素之间插入,也可以在第线性DP 训练 codeforces gzcl线性训练计划_链表_03个元素之前插入。
  2. 判断顺序表的存储空间是否已满。
  3. 将第线性DP 训练 codeforces gzcl线性训练计划_线性DP 训练 codeforces_04至第线性DP 训练 codeforces gzcl线性训练计划_链表个元素依次向后移动一个位置,空出第线性DP 训练 codeforces gzcl线性训练计划_链表个位置。
  4. 将要插入的新元素线性DP 训练 codeforces gzcl线性训练计划_顺序表_07放入第线性DP 训练 codeforces gzcl线性训练计划_链表个位置。
  5. 表长加1。

实现

#include <iostream>
using namespace std;
#define Maxsize 100//最大空间
typedef struct{
    int *elem;
    int length;//顺序表的长度
}SqList;//用typedef将结构体等价于类型名
bool ListInsert_Sq(SqList &L,int i,int e);//插入数值
void InitList(SqList &L);//初始化顺序表
void AssginList(SqList &L,int x[],int size);//为顺序表赋值
void show(const SqList &L);//打印数值元素
int main() {
    SqList cs;
    InitList(cs);
    int num[8] = {3,5,6,7,2,8,10,1};
    AssginList(cs,num,8);
    show(cs);
    ListInsert_Sq(cs,4,9);
    show(cs);
}
bool ListInsert_Sq(SqList &L,int i,int e){
    if (i<1 || i>L.length+1){//判断i值是否合法
        return false;
    }
    if (L.length == Maxsize){//判断存储空间是否足够
        return false;
    }
    for (int j = L.length-1; j >= i-1; --j) {
        L.elem[j+1] = L.elem[j];//从最后一个元素开始后移,直到第i个元素后移
    }
    L.elem[i-1] = e;//将新元素e放入第i个位置
    L.length++;//表长加1
    return true;
}

void InitList(SqList &L) {
    L.elem = new int[10];
    L.length = 0;
}
void AssginList(SqList &L,int x[],int size) {
    for (int i = 0; i < size; i++) {
        L.elem[i] = x[i];
        L.length++;
    }
}
void show(const SqList &L){
    for (int i = 0; i < L.length; ++i) {
        cout << L.elem[i] << " ";
    }
    cout <<endl;
}

输出:

3 5 6 7 2 8 10 1
3 5 6 9 7 2 8 10 1

删除

步骤

  1. 判断插入位置是否合法线性DP 训练 codeforces gzcl线性训练计划_线性DP 训练 codeforces_02
  2. 将欲删除的元素保留在线性DP 训练 codeforces gzcl线性训练计划_顺序表_07中。
  3. 将第线性DP 训练 codeforces gzcl线性训练计划_链表_11至第线性DP 训练 codeforces gzcl线性训练计划_链表_12个元素依次向前移动一个位置。
  4. 表长减1,若删除成功则返回true

实现

#include <iostream>
using namespace std;
typedef struct{
    int *elem;
    int length;//顺序表的长度
}SqList;//用typedef将结构体等价于类型名
bool ListDelete_Sq(SqList &L,int i,int &e);//删除元素
void InitList(SqList &L);//初始化顺序表
void AssginList(SqList &L,int x[],int size);//为顺序表赋值
void show(const SqList &L);//打印数值元素
int main() {
    SqList cs;
    InitList(cs);
    int num[8] = {3,5,6,7,2,8,10,1};
    AssginList(cs,num,8);
    show(cs);
    int bl;//保留删除元素
    ListDelete_Sq(cs,4,bl);
    show(cs);
    cout << bl;
}
bool ListDelete_Sq(SqList &L,int i,int &e){
    if (i<1 || i>L.length){//判断i值是否合法
        return false;
    }
    e = L.elem[i-1];
    for (int j = i; j <= L.length-1; ++j) {
        L.elem[j-1] = L.elem[j];
    }
    L.length--;
    return true;
}

void InitList(SqList &L) {
    L.elem = new int[10];
    L.length = 0;
}
void AssginList(SqList &L,int x[],int size) {
    for (int i = 0; i < size; i++) {
        L.elem[i] = x[i];
        L.length++;
    }
}
void show(const SqList &L){
    for (int i = 0; i < L.length; ++i) {
        cout << L.elem[i] << " ";
    }
    cout <<endl;
}

输出:

3 5 6 7 2 8 10 1
3 5 6 2 8 10 1
7

单链表

  1. 链表是线性表的链式存储方式,可以给每个元素都附加一个指针于,指向下一个元素的存储位置。
  2. 每个节点都包含两个域:数据域和指针域。数据域存储数据元素,指针域存储下一个节点的地址,因此指针指向的类型也是节点类型。
  3. 在单链表中找第i个元素,必须从头开始,按顺序一个一个地找,一直数到第i个元素,被称为顺序存取。
  4. 单链表节点结构体定义:
typedef struct Lnode{
	int data;
	struct Lnode *next;//指向下一个节点的指针
}Lnode,*Linklist;
//用typedef将结构体等价于类型名Lnode,指针Linklist

插入

在第i个结点之前插入元素e,相当于在第i-1个节点之后插入元素e。假设已找到第i-1个节点,并用p指针指向该节点,s指向待插入的新节点。

#include <iostream>
using namespace std;
typedef struct Lnode{
    int data;
    struct Lnode *next;
}Lnode,*LinkList;
bool ListInstert_L(LinkList &L,int i,int e);//元素插入函数
void show( const LinkList L);//打印链表内容函数
int main(){
    Lnode s1,s2;
    LinkList p = &s1;
    s1.data = 10;
    s1.next = &s2;

    s2.data = 13;
    s2.next = NULL;
    show(p);
    ListInstert_L(p,1,15);//将15插入链表中
    show(p);
    ListDelete_L(p,2);
    show(p);
}
bool ListInstert_L(LinkList &L,int i,int e){
    int j;
    LinkList p, s;
    p = L;
    j = 0;
    while (p && j<i-1){//查找第i-1个节点,p指向该节点
        p = p->next;
        j++;
    }
    if(!p || j>i-1){
        return false;
    }
    s = new Lnode;//生成新节点
    s->data = e;//将数据元素e放入新节点的数据域中
    s->next = p->next;//将新节点的指针域指向第i个节点
    p->next = s;//将节点p的指针域指向节点s
}
void show(const LinkList L){
    LinkList cs = L;
    while(cs!=NULL)
    {
        cout<< cs->data <<" ";
        cs = cs->next;
    }
    cout << endl;
}

输出:

10 13
10 15 13

删除

删除一个节点实际上是把这个结点跳过去。根据单向链表向后操作的特性,想要跳过第i个节点,就必须先找到第i-1个节点,否则无法跳过。

#include <iostream>
using namespace std;
typedef struct Lnode{
    int data;
    struct Lnode *next;
}Lnode,*LinkList;
bool ListInstert_L(LinkList &L,int i,int e);//元素插入函数
bool ListDelete_L(LinkList &L,int i);//元素删除函数
void show( const LinkList L);//打印链表内容函数
int main(){
    Lnode s1,s2;
    LinkList p = &s1;
    s1.data = 10;
    s1.next = &s2;

    s2.data = 13;
    s2.next = NULL;
    show(p);
    ListInstert_L(p,1,15);//将15插入链表中
    show(p);
    ListDelete_L(p,2);
    show(p);
}
bool ListInstert_L(LinkList &L,int i,int e){
    int j;
    LinkList p, s;
    p = L;
    j = 0;
    while (p && j<i-1){//查找第i-1个节点,p指向该节点
        p = p->next;
        j++;
    }
    if(!p || j>i-1){
        return false;
    }
    s = new Lnode;//生成新节点
    s->data = e;//将数据元素e放入新节点的数据域中
    s->next = p->next;//将新节点的指针域指向第i个节点
    p->next = s;//将节点p的指针域指向节点s
}

bool ListDelete_L(LinkList &L,int i){
    LinkList p,q;
    int j;
    p = L;
    j = 0;
    while((p->next) && (j<j-1)){//查找第i-1个节点,p指向该节点
        p = p->next;
        j++;
    }
    if(!(p->next) || (j>i-1)){ //当i>n或i<1时,删除位置不合理
        return false;
    }
    q = p -> next;//临时保存被删除节点的地址以备释放空间
    p->next = q->next;//将节点q的下一个结点地址赋值给节点p的指针域
    delete q;//释放被删除节点的空间
    return true;
}

void show(const LinkList L){
    LinkList cs = L;
    while(cs!=NULL)
    {
        cout<< cs->data <<" ";
        cs = cs->next;
    }
    cout << endl;
}

输出:

10 13
10 15 13
10 13

双向链表

  1. 在双向链表中,每个元素都附加了两个指针域,分别指向前驱结点和后继结点,一个存储前一个元素的地址,一个存储下一个元素的地址,方便向前、向后操作方便。
  2. 双向链表的节点结构体定义如下:
typedef struct DuLnode{
    int data;
    struct DuLnode *prior,*next; //指向前驱节点与后继节点的指针
}DuLnode,*DuLinklist;

插入:

#include<bits/stdc++.h>
using namespace std;
typedef struct DuLnode{
    int data;
    struct DuLnode *prior,*next; //指向前驱节点与后继节点的指针
}DuLnode,*DuLinklist;
void CreateTailList(DuLinklist &L, int n); //创建双向链表
bool ListInsert_L(DuLinklist &L,int i,int e); //插入元素
void show(DuLinklist L); //遍历链表并打印
int main()
{
    DuLinklist c;
    CreateTailList(c,4);
    show(c);
    ListInsert_L(c,2,8);
    show(c);
}
bool ListInsert_L(DuLinklist &L,int i,int e){
    int j;
    DuLinklist p,s;
    p = L;
    j = 0;
    while(p && j<i){ //查找第i个节点,p指向该节点
        p = p -> next;
        j++;
    }
    if(!p || j>i){ //i>n+1 或者 i<1
        return false;
    }
    s = new DuLnode; //生成新节点
    s->data = e; //将新节点的数据域置为e
    p->prior->next = s;
    s->prior = p->prior;
    s->next = p;
    p->prior = s;
    return true;
}
void show(DuLinklist L){
    DuLinklist cs = L;
    while(cs -> next != NULL){
        cout<< cs->next->data <<" ";
        cs = cs->next;
    }
    cout << endl;
}
void CreateTailList(DuLinklist &L, int n){//创建大小长度为n的链表
    DuLinklist p, r;
    L = new DuLnode;
    L->next = NULL;
    L->prior = NULL;
    L->data = n;
    r = L;
    int i = 0;
    for(i = 0; i < n; i ++){
        p = new DuLnode;
        p->data = i;
        r->next = p;
        p->prior = r;
        r = p;
    }
    r->next = NULL;
}

输出:

0 1 2 3
0 8 1 2 3

删除:

#include<bits/stdc++.h>
using namespace std;
typedef struct DuLnode{
    int data;
    struct DuLnode *prior,*next; //指向前驱节点与后继节点的指针
}DuLnode,*DuLinklist;
void CreateTailList(DuLinklist &L, int n); //创建双向链表
bool ListInsert_L(DuLinklist &L,int i,int e); //插入元素
bool ListDelete_L(DuLinklist &L,int i); //删除元素
void show(DuLinklist L); //遍历链表并打印
int main()
{
    DuLinklist c;
    CreateTailList(c,4);
    show(c);
    ListInsert_L(c,2,8);
    show(c);
    ListDelete_L(c,1);
    show(c);
}
void CreateTailList(DuLinklist &L, int n){//创建大小长度为n的链表
    DuLinklist p, r;
    L = new DuLnode;
    L->next = NULL;
    L->prior = NULL;
    L->data = n;
    r = L;
    int i = 0;
    for(i = 0; i < n; i ++){
        p = new DuLnode;
        p->data = i;
        r->next = p;
        p->prior = r;
        r = p;
    }
    r->next = NULL;
}
bool ListInsert_L(DuLinklist &L,int i,int e){
    int j;
    DuLinklist p,s;
    p = L;
    j = 0;
    while(p && j<i){ //查找第i个节点,p指向该节点
        p = p -> next;
        j++;
    }
    if(!p || j>i){ //i>n+1 或者 i<1
        return false;
    }
    s = new DuLnode; //生成新节点
    s->data = e; //将新节点的数据域置为e
    p->prior->next = s;
    s->prior = p->prior;
    s->next = p;
    p->prior = s;
    return true;
}
bool ListDelete_L(DuLinklist &L,int i){
    DuLinklist p;
    int j;
    p = L;
    j = 0;
    while(p && (j<i)){//查找第i个节点,p指向该节点
        p = p->next;
        j++;
    }
    if(!p || (j>i)){//当i>n或i<1时,删除位置不合理
        return false;
    }
    if(p->next){ //如果p的后继节点存在
        p->next->prior = p->prior;
    }
    p->prior->next = p->next;
    delete p; //释放被删除节点的空间
    return true;
}
void show(DuLinklist L){
    DuLinklist cs = L;
    while(cs -> next != NULL){
        cout<< cs->next->data <<" ";
        cs = cs->next;
    }
    cout << endl;
}

输出:

0 1 2 3
0 8 1 2 3
8 1 2 3

训练1:区块世界

在早期的人工智能规划和机器人研究中使用了一个区块世界,在这个世界中,机器人手臂执行设计区块操作的任务。问题是要解析一系列命令,这些命令指导机器人手臂如何操作平板上的块。最初,有线性DP 训练 codeforces gzcl线性训练计划_链表_13个区块(编号为线性DP 训练 codeforces gzcl线性训练计划_算法_14),对于所有线性DP 训练 codeforces gzcl线性训练计划_算法_15的情况,区块线性DP 训练 codeforces gzcl线性训练计划_算法_16与区块线性DP 训练 codeforces gzcl线性训练计划_链表_17相邻。

用于操纵块的有效命令如下

  • move a onto b:把ab上方的块全部放回初始位置,然后把a放到b上方。
  • move a over b:把a上方的块全部放回初始位置,然后把a放到b所在块堆的最上方。
  • pile a onto b:把b上方的块全部放回初始位置,然后把aa上方所有的块整体放到b上方。
  • pile a over b:把aa上方所有的块整体放到b所在块堆的最上方。
  • quit:结束标志。

任何a = bab在同一块堆中的命令都是非法命令。所有非法命令都应被忽略。

输入:输入的第1行为整数线性DP 训练 codeforces gzcl线性训练计划_链表_13线性DP 训练 codeforces gzcl线性训练计划_算法_19,表示区块时间中的块数。后面是一系列块命令,每行一个命令。在遇到quit命令之前,程序应该处理所有命令。所有命令都将采用上面指定的格式,不会有语法错误的命令。

输出:输出应该包含区块世界的最终状态。每一个区块i(0 \leq i <n)后面都有一个冒号。如果上面至少有一个块,则冒号后面必须跟一个空格,后面跟一个显式在该位置的块列表,每个块号与其他块号之间用空格隔开,不要在行末加空格。

#include <iostream>
#include <vector>
using namespace std;
vector<int> block[30];
int n;
void init();//输入函数
void loc(int x, int &p, int &h);//找位置
void goback(int p, int h);//将p块堆高度大于h的所有块归位
void moveall(int p, int h, int q);//将p块堆高度大于或等于h的所有块都移动到q块堆的上方
void solve();//判断块操作函数
int main() {
    init();
    solve();
    for (int i = 0; i < n; ++i) {
        cout << i << ":";
        for (int j = 0; j < block[i].size(); ++j) {
            cout << " "<<block[i][j];
        }
        cout << endl;
    }
}
void init(){
    cin >> n;
    for (int i = 0; i < n; ++i) {
        block[i].push_back(i);
    }
}

void loc(int x, int &p, int &h){
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < block[i].size(); ++j) {
            if(block[i][j] == x){
                p = i;//块堆
                h = j;//高度
            }
        }
    }
}

void goback(int p, int h){
    for (int i = h+1; i < block[p].size(); ++i) {
        int k = block[p][i];
        block[k].push_back(k);//归位
    }
    block[p].resize(h+1);//重置大小
    //resize()传递两个参数,分别是大小和初始值,初始值默认为0
}

void moveall(int p, int h, int q){
    for (int i = h; i < block[p].size(); ++i) {
        int k = block[p][i];
        block[q].push_back(k);
    }
    block[p].resize(h);//重置大小
}

void solve(){
    int a,b;
    string s1,s2;
    while(cin >> s1){
        if(s1 == "quit"){
            break;
        }
        cin >> a >> s2 >> b;
        int ap = 0,ah = 0,bp = 0,bh = 0;
        loc(a,ap,ah);//
        loc(b,bp,bh);//
        if(ap == bp){
            continue;
        }
        if(s1 == "move"){//a归位
            goback(ap,ah);
        }
        if(s2 == "onto"){//b归位
            goback(bp,bh);
        }
        moveall(ap,ah,bp);//将a块堆高度大于h的所有块都移动到b块堆上。
    }
}

输入:

10
move 9 onto 1
move 8 over 1
move 7 over 1
move 6 over 1
pile 8 over 6
pile 8 over 5
move 2 over 1
move 4 over 9
quit

输出:

0: 0
1: 1 9 2 4
2:
3: 3
4:
5: 5 8 7 6
6:
7:
8:
9:

训练2 悲剧文本

假设你在用坏键盘键入一个长文本。键盘的唯一问题是有时HomeEnd键会自动按下(内部)。你没有一时到这个问题,因为你只关注文本,甚至没有打开显示器!输入完毕后,你才发现屏幕上显示的是一段悲剧文本。你的任务是找到悲剧文本。

输入:有几个测试用例。每个测试用例各占一行,包含至少一个且最多100000个字母、下画线和两个特殊字符[][表示内部按了Home键,]表示内部按下了End键。输入由文件结尾EOF终止

输出:对于每种情况,都在屏幕上输出悲剧文本

算法设计

本题一直在头部和尾部进行操作,使用顺序存储时需要移动大量的元素,因此可以考虑双向链表。不需要移动元素,直接进行插入操作。在C++的STL中,list是一个双向链表,可以快速在头尾进行操作。

  1. 定义一个字符类型的list,链表名为text
  2. 定义一个迭代器it,指向链表的开头。
  3. 检查字符串,如果遇到[,则指向链表开头,即it = text.begin();如果遇到],则指向链表尾部,即it = text.end()
  4. 如果是正常文本,则执行插入操作。
#include <iostream>
#include <list>;
using namespace std;
void solve(string s);
int main(){
    string temp;
    while(getline(cin,temp) && !cin.eof()){
        solve(temp);
    }
}
void solve(string s){
    int len = s.size();
    list<char> text;
    auto it = text.begin();
    for(int i = 0; i<len; i++){
        if(s[i] == '['){
            it = text.begin();
        }
        else if(s[i] == ']'){
            it = text.end();
        }
        else{
            it = text.insert(it,s[i]);
            it++;
        }
    }
    for (it = text.begin(); it!=text.end(); it++) {
        cout<<*it;
    }
    s.clear();
    cout << endl;
}

输入:

Tis_is_a_[Beiju]_text
[[]][][]Happy_Birthday_to_Tsinghua_University

输出:

BeijuTis_is_a__text
Happy_Birthday_to_Tsinghua_University

训练3 移动盒子

一行有线性DP 训练 codeforces gzcl线性训练计划_链表_13个盒子,从左到右编号为线性DP 训练 codeforces gzcl线性训练计划_顺序表_21。模拟以下4种命令。

  • 1 线性DP 训练 codeforces gzcl线性训练计划_线性DP 训练 codeforces_22 线性DP 训练 codeforces gzcl线性训练计划_算法_23:将盒子线性DP 训练 codeforces gzcl线性训练计划_线性DP 训练 codeforces_22移动到线性DP 训练 codeforces gzcl线性训练计划_算法_23的左侧(如果线性DP 训练 codeforces gzcl线性训练计划_线性DP 训练 codeforces_22已经在线性DP 训练 codeforces gzcl线性训练计划_算法_23的左侧,则忽略此项)
  • 2 线性DP 训练 codeforces gzcl线性训练计划_线性DP 训练 codeforces_22 线性DP 训练 codeforces gzcl线性训练计划_算法_23:将盒子线性DP 训练 codeforces gzcl线性训练计划_线性DP 训练 codeforces_22移动到线性DP 训练 codeforces gzcl线性训练计划_算法_23的右侧(如果线性DP 训练 codeforces gzcl线性训练计划_线性DP 训练 codeforces_22已经在线性DP 训练 codeforces gzcl线性训练计划_算法_23的右侧,则忽略此项)
  • 3 线性DP 训练 codeforces gzcl线性训练计划_线性DP 训练 codeforces_22 线性DP 训练 codeforces gzcl线性训练计划_算法_23:交换盒子线性DP 训练 codeforces gzcl线性训练计划_线性DP 训练 codeforces_22线性DP 训练 codeforces gzcl线性训练计划_算法_23的位置。
  • 4:翻转整行盒子序列

以上命令保证有效,即线性DP 训练 codeforces gzcl线性训练计划_算法_38不等于线性DP 训练 codeforces gzcl线性训练计划_线性DP 训练 codeforces_39
输入:最多有10个测试用例。每个测试用例的第1行都包含两个整数线性DP 训练 codeforces gzcl线性训练计划_链表_13线性DP 训练 codeforces gzcl线性训练计划_顺序表_41线性DP 训练 codeforces gzcl线性训练计划_数据结构_42),下面的线性DP 训练 codeforces gzcl线性训练计划_顺序表_41行,每行都包含一个命令

输出:对于每个测试用例,都单行输出奇数索引位置的数字总和。

算法设计

本题涉及大量移动元素,使用链表比较合适,但查找是链表不擅长的,多次查找会超时,所以不能使用list链表实现,应选择静态双向链表。

  1. 初始化双向静态链表(前驱数组为l[],后继数组为r[]),翻转标记flag = false
  2. 读入操作指令a。
  3. 如果a = 4,则标记翻转,flag = !flag,否则读入x、y。
  4. 如果a!=3&&flag,则a = 3-a。因为如果翻转标记为真,则左右是倒置的,1、2指令正好相反,即1号指令(将x移到y左侧)相当于2号指令(将x移到y右侧)。因此如果a = 1则转换为2;如果a = 2则转换为1。
  5. 对于1、2指令,如果本来位置就是对的,则说明都不做。
  6. 如果a = 1,则删除x,将x插入y左侧
  7. 如果a = 2,则删除x,将x插入y右侧
  8. 如果a = 3,则考虑相邻和不相邻两种情况进行处理。
#include<bits/stdc++.h>
using namespace std;
int r[100000+5],l[100000+5];
void init(int n); //链接函数
void link(int L,int R); //删除函数
int main()
{
    int n,m,a,x,y,k=0;
    bool flag;
    while(cin>>n>>m)
    {
        flag=false;
        init(n);
        for(int i=0;i<m;i++)
        {
            cin>>a;
            if(a==4)
                flag=!flag;//翻转
            else
            {
                cin>>x>>y;
                if(a==3&&r[y]==x) swap(x,y);
                if(a!=3&&flag)
                    a=3-a;
                if(a==1&&x==l[y])
                    continue;
                if(a==2&&x==r[y])
                    continue;
                int Lx=l[x],Rx=r[x],Ly=l[y],Ry=r[y];
                if(a==1)
                {
                    link(Lx,Rx);//删除x
                    link(Ly,x);
                    link(x,y);//x插入y左
                }
                else if(a==2)
                {
                    link(Lx,Rx);//删除x
                    link(y,x);
                    link(x,Ry);//x插入y右
                }
                else if(a==3)
                {
                    if(r[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 t=0;
        long long sum=0;
        for(int i=1;i<=n;i++)
        {
            t=r[t];
            if(i%2==1)
                sum+=t;
        }
        if(flag&&n%2==0)
            sum=(long long)n*(n+1)/2-sum;
        cout<<"Case "<<++k<<": "<<sum<<endl;
    }
    return 0;
}

void init(int n)
{
    for(int i=1;i<=n;i++)
    {
        l[i]=i-1;
        r[i]=(i+1)%(n+1);
    }
    r[0]=1;
    l[0]=n;
}

void link(int L,int R) //将L和R链接起来
{
    r[L]=R;
    l[R]=L;
}

输入:

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

输出:

Case 1: 12
Case 2: 9
Case 3: 2500050000