Luogu P2596 [ZJOI2006]书架

题目大意

给定一个长度为 n n n 的序列,你需要完成 n n n 个操作,共有 5 5 5 种操作。

操作说明
T o p   S Top\ S Top S S S S 移动到序列最前面
B o t t o m   S Bottom\ S Bottom S S S S 移动到序列最后面
I n s e r t   S   T Insert\ S\ T Insert S T S S S 向后移动 T T T 个位置,其中 T ∈ { 1 , 0 , − 1 } T\in\{1,0,-1\} T{1,0,1}
A s k   S Ask\ S Ask S询问 S S S 是序列中的第几个元素
Q u e r y   S Query\ S Query S询问序列中从左往右数的第 S S S 个元素

数据范围  1 ⩽ n , m ⩽ 80000 1 \leqslant n,m \leqslant 80000 1n,m80000

题解

需要求排名,考虑权值线段树 / / /树状数组 / S p l a y /Splay /Splay
需要修改序列,考虑 S p l a y Splay Splay
由于元素只有 80000 80000 80000 个,因此对于 T o p Top Top 操作和 B o t t o m Bottom Bottom 操作,直接删除原位置的节点,在 S p l a y Splay Splay 最后 / / /前面插入新节点。
对于 I n s e r t Insert Insert 操作其实可以直接交换两节点的 i d id id,而不需要做其他修改。
对于 Q u e r y Query Query 操作,便是求排名, S p l a y Splay Splay 维护 s i z e size size 域即可。
重点是 A s k Ask Ask 操作。这里用了一个小 t r i c k trick trick,类似 s e t set set i n s e r t insert insert ,我们可以在 i n s e r t insert insert 的结尾返回插入的节点的指针,这样就可以完美解决 A s k Ask Ask 操作,但要注意其余修改了节点的操作都要连着一起做改动。

代码

ZZY调了半天还没调出来,我由于封装得好,一遍过……

// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
template<class T> struct node{
    int size,n;
	T data;
    node *fa,*ch[2];
    inline node(T d=T(),node *f=NULL):data(d),size(1),n(1),fa(f){
        ch[0]=ch[1]=NULL;
    }
    inline void up(){
        size=n;
        if(ch[0]) size+=ch[0]->size;
        if(ch[1]) size+=ch[1]->size;
    }
};
#define which(p) (p->fa->ch[1]==p)
template<class T> struct Splay{
    static const int maxn=80010;
    int top;
    node<T> *root;
    node<T> *pool[maxn<<1];//内存池 
    inline Splay():top(0){
        for(int i=0;i<maxn;i++)
            pool[i]=new node<T>();
    }
    inline void newnode(node<T> *&p,T data,node<T> *fa){
        p=pool[top++];
        *p=node<T>(data,fa);
    }
    inline void dels(node<T> *&p){
        pool[--top]=p;
        p=NULL;
    }
    inline node<T> *&down(node<T> *&p,T x){
        if(x==p->data)
            return p;
        return p->ch[p->data<x];
    }
    inline void rotate(node<T> *p){
        int t=which(p);
        node<T> *f=p->fa;
        if(f->ch[t]=p->ch[t^1])
            p->ch[t^1]->fa=f;
        if(p->fa=f->fa)
            p->fa->ch[which(f)]=p;
        f->fa=p;p->ch[t^1]=f;
        f->up();p->up();
        if(p->fa==NULL)
            root=p;
    }
    inline void splay(node<T> *p,node<T> *Fa){
        for(;p->fa!=Fa;rotate(p))
            if(p->fa->fa!=Fa)
                rotate(which(p)==which(p->fa)?p->fa:p);
    }//三行Splay不想要吗? 
    inline node<T>* insert(T val){
		//其实插入可以删去,一开始就构造一颗完全二叉树
		//但考虑到后面需要不断地Splay,对时间的影响不大 
    	node<T> *p=root;
        if(root==NULL){
            newnode(root,val,NULL);
            return root;
        } node<T> *fa=p->fa;
        while(p!=NULL){
            node<T> *t=down(p,val);
            if(t==p)
                break;
            fa=p;p=t;
        } if(p==NULL){
            newnode(p,val,fa);
            down(fa,val)=p;
        }else
            ++p->n;
        splay(p,NULL);
        return p;
    }
    inline node<T> *kth(int k){//求第k大 
    	node<T> *p=root;
        while(p){
            int d=p->ch[0]?p->ch[0]->size:0;
            if(k>=d+1&&k<=d+p->n){
                splay(p,NULL);
                return p;
            }
            if(k<d+1) p=p->ch[0];
            else k-=d+p->n,p=p->ch[1];
        }
        return NULL;
    }
    inline node<T>* begin(node<T> *p){//求开头 
        if(!p) return NULL;
        while(p->ch[0]) p=p->ch[0];
        return p;
    }
    inline node<T>* end(node<T> *p){//求结尾 
        if(!p) return NULL;
        while(p->ch[1]) p=p->ch[1];
        return p;
    }
    inline node<T> *merge(node<T> *a,node<T> *b){//合并Splay 
        if(b==NULL) return a;
        if(a==NULL) return b;
        node<T> *t=end(a);
        splay(t,NULL);
        t->ch[1]=b;
        b->fa=t;
        t->up();
        return t; 
    }
    inline void del(node<T> *pos){//删除 
        if(pos==NULL)
            return ;
        if(pos->n>1){
            --pos->n;
            return;
        } splay(pos,NULL);
        pos=root;
        dels(root);
        root=merge(pos->ch[0],pos->ch[1]);
        if(root!=NULL)
            root->fa=NULL;
    }
    inline int rank(node<T> *pos){//求排名 
    	splay(pos,NULL);
    	if(pos->ch[0])
    		return pos->ch[0]->size+1;
    	return 1;
	}
    inline node<T>* pre(node<T> *x){
    	splay(x,NULL);
    	return end(x->ch[0]);
	}
	inline node<T>* nxt(node<T> *x){
    	splay(x,NULL);
    	return begin(x->ch[1]);
	}
    void move_front(node<T>*&);
    void move_back(node<T>*&);
};
template<class T> void Splay<T>::move_back(node<T>* &x){
	T tmps=x->data;del(x);//先删除 
	node<T> *p=end(root);
	tmps.d=p->data.d+1;//调整次序 
	newnode(p->ch[1],tmps,p);//加入新节点 
	splay(p->ch[1],NULL);
	x=root;
}
template<class T> void Splay<T>::move_front(node<T>* &x){
	T tmps=x->data;del(x);
	node<T> *p=begin(root);
	tmps.d=p->data.d-1;
	newnode(p->ch[0],tmps,p);
	splay(p->ch[0],NULL);
	x=root;
}
template<class T> void swaps(node<T>* &a,node<T>*& b){
	swap(a->data.id,b->data.id);
	swap(a,b);//连着point数组一起换掉 
}
struct datas{
	int id,d;
	datas(int ids=0,int da=0):id(ids),d(da){}
	bool operator==(const datas &x)const{return d==x.d;}
	bool operator<(const datas &x)const{return d<x.d;}
};
Splay<datas> t;
node<datas> *point[80010];
int n,m;
char str[110];
int main(void){
	scanf("%d%d",&n,&m);
	for(int i=1,x;i<=n;i++){
		scanf("%d",&x);
		point[x]=t.insert(datas(x,i));
	}
	for(int i=1,x,y;i<=m;i++){
		scanf("%s%d",str,&x);
		if(str[0]=='T') t.move_front(point[x]);
		else if(str[0]=='B') t.move_back(point[x]);
		else if(str[0]=='A') printf("%d\n",t.rank(point[x])-1);
		else if(str[0]=='Q') printf("%d\n",t.kth(x)->data.id);
		else{
			scanf("%d",&y);
			if(y==0) continue;
			if(y==1) swaps(point[x],point[t.nxt(point[x])->data.id]);
			else swaps(point[x],point[t.pre(point[x])->data.id]);
			//连着point数组一起换掉(point[t->data.id]==t) 
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值