uva12657-题解

题干

你有n个盒子在桌子上的一条线上从左到右编号为1……n。你的任务是模拟四种操作

1 X Y 移动盒子编号X到盒子编号Y的左边(如果X已经在Y的左边了就忽略)

2 X Y 移动盒子编号X到盒子编号Y的右边(如果X已经在Y的右边了就忽略)

3 X Y 交换盒子编号X与盒子编号Y的位置

4 将整条线反转

操作保证合法,X不等于Y

输入

最多有10组数据,每个数据会包含两个整数n,m(1≤n,m<100,000), 接下来是m行数据,表示操作。

输出

对于每组数据,输出他们奇数位置的编号的和。

思路

对于一串数,我们需要频繁的插入移动数据,宜使用双向链表,(详见双向链表)
这里头节点序号是0,是为了方便init里面for循环也满足
思路很简单,写出每个操作的函数即可,有:
move(o1),删除x左右两个节点和x的关系,并让x和y,y左边节点链接(4个指针操作)
enchange(o1),特殊讨论相邻情况(没讨论会导致自身指针,TLE原因之一),不相邻记录x,y两边节点,交换关系(4*2次指针操作)
reverse(on),从第一个节点开始,交换左右指针,交换完了,此时除了头尾节点都成功翻转,之后更新头尾节点及其相邻指向即可。
值得注意的是,出现多次TLE,排除了循环和自身指针的情况,我们考虑算法时间复杂度的问题
由于move,exchange,都是o1复杂度,我们考虑优化reverse();
注意到一串数进行反转操作,在进行x插入到y左边,等同于x插入到y右边,再进行反转
也就是反转操作可以提后,放在最后再抵消!然后输出奇数位,如果反转,只需要倒着输出即可
所以我们甚至不用写reverse函数!
只需要一个reversed标志,如果为true,执行假定反转的操作即可
修改逻辑,取消reverse,ac

代码实现

#include<iostream>
#include<sstream>
using namespace std;
const int MAXN=100010;
void init(int n);
void insert(int a,int num);
void delete_(int a);
void move1(int x,int y);
void move2(int x,int y);
void exchange(int x,int y);
int idx=2;
int l[MAXN],r[MAXN],e[MAXN];
bool reversed=false;
int main(){
    int n,m;
    int times=0;
    while(cin>>n>>m){
        times++; 
        idx=2;      
        r[1]=0,l[0]=1;
        init(n);
        int j=m;
        reversed=false;
        while(j--){
            int num,x,y;
            scanf("%d",&num);
            if(num!=4)scanf("%d %d",&x,&y);                
            switch(num){ 
                case 1:if(reversed){move2(x+1,y+1);break;}else{move1(x+1,y+1);break;}
                case 2:if(reversed){move1(x+1,y+1);break;}else{move2(x+1,y+1);break;}
                case 3:exchange(x+1,y+1);break;
                case 4:reversed=!reversed;break;
            }
            
        }
        long long sum=0;
        bool add = true;
        if(!reversed)for(int i = r[1]; i != 0; i = r[i]){
            if(add) sum += e[i];
            add = !add;
        }
        else{
            for(int i = l[0]; i != 1; i = l[i]){
            if(add) sum += e[i];
            add = !add;
        }
        }
        cout<<"Case "<<times<<": "<<sum<<endl;
    }
    return 0;
}
void init(int n){
    for(int i=1;i<=n;i++)
        insert(i,i);
//编号i节点插入的是i-1
}
void insert(int a,int num){
    //在a后插入
    e[idx]=num;
    l[idx]=a,r[idx]=r[a];
    l[r[a]]=idx,r[a]=idx;
    idx++;
}
void delete_(int a){
    if(a==0||a==1)return;
    r[l[a]]=r[a];
    l[r[a]]=l[a];
}
void move1(int x,int y){
    if(x==l[y])return ;
    delete_(x);
    int z=l[y];
    l[x]=z,r[x]=y;
    r[z]=x,l[y]=x;
}
void move2(int x,int y){
    if(x==r[y])return ;
    delete_(x);
    int z=r[y];
    l[x]=y,r[x]=z;
    r[y]=x,l[z]=x;
}
void exchange(int x,int y){    
    if (r[x] == y) {
        int lx = l[x], ry = r[y];
        r[lx] = y; l[y] = lx;
        r[y] = x; l[x] = y;
        r[x] = ry; l[ry] = x;
        return ;
    }
    else if (r[y] == x) {
        exchange(y, x);  // 直接调用上面情况
        return ;
    }
    int lx=l[x],rx=r[x];
    int ly=l[y],ry=r[y];
    l[x]=ly,r[x]=ry;
    r[ly]=x;l[ry]=x;
    l[y]=lx,r[y]=rx;
    r[lx]=y,l[rx]=y;
}
posted @ 2025-08-03 18:06  hardestnut  阅读(4)  评论(0)    收藏  举报