HDU 3436:splay tree

给一个1~n的序列,有三种操作:

1.TOP x :把x移到序列首部

2.Query x:询问x的位置

3.Rank x:询问第k个位置的元素

 

总而言之就是一个序列维护的问题,用splay tree可以解决。但是首先要解决的问题就是把题目中的操作转化为splay tree的基本操作

1. Top x:可以理解为把序列中的x找到并删除,然后再插入到序列首部

2.Query x:找到x在树中的位置并输出其在序列中的位置

3.Rank x:在树中找到维护的第x个数并输出

 

常规来说,我们是在splay tree中一个结点维护原序列中的一个数,但是这里n有10^8,数组是开不下的,但是q只有10^5,我们可以想想如何离散化

这里是把query 和top对应的x单独维护,其余部分作为一个区间放置进一个结点,这样可以保证结点数在10^5内

为什么要这样做呢?首先对于top来说,我们是要做的是删除和插入操作,这里的操作对象只有x一个值,所以单独列出以免影响序列其他部分

再一个就是query操作,我们查询位置的话在splay tree中的操作往往就是把x旋转到树根,那左子树的大小也就对应了x的在原序列的下标。如果x不是单独列出,那这一条定律显然是不成立的

至于Rank,就相当于是在从左往右数数咯,可以一段一段数~~

#include"cstdio"
#include"queue"
#include"cmath"
#include"stack"
#include"iostream"
#include"algorithm"
#include"cstring"
#include"queue"
#include"map"
#include"vector"
#define ll long long
#define mems(a,b) memset(a,b,sizeof(a))

using namespace std;
const int MAXN = 200500;
const int MAXE = 200500;
const int INF = 0x3f3f3f;

int pre[MAXN],ch[MAXN][2],siz[MAXN],num[MAXN],key[MAXN],ask[MAXN],p[MAXN],e[MAXN],s[MAXN];
int root,tot,tail,n,P=1,cnt,T,q;

struct Node{
    char op;
    int x;
}node[MAXN];

void pushup(int x){
    siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+num[x];
}

void init(){
    tot=0;
    root=0;
    cnt=0;
    tail=0;
    siz[0]=pre[0]=ch[0][0]=ch[0][1]=0;
}

void newnode(int &pos,int fa,int k){///k是在原数列(离散化后)中的位置,pos为在splay tree中的结点编号
    pos=++tot;
    pre[pos]=fa;
    p[k]=pos;           ///p[原序列位置]=树中位置
    key[pos]=k;         ///key[树中位置]=原序列中位置
    ch[pos][0]=ch[pos][1]=0;
    siz[pos]=num[pos]=e[k]-s[k]+1;
}

void build(int &x,int l,int r,int fa){
    if(l>r) return;
    int mid=(l+r)>>1;
    newnode(x,fa,mid);
    build(ch[x][0],l,mid-1,x);
    build(ch[x][1],mid+1,r,x);
    pushup(x);
}

void Rotate(int x,int kind){
    int fa=pre[x];
    ch[fa][!kind]=ch[x][kind];
    pre[ch[x][kind]]=fa;
    if(pre[fa]) ch[pre[fa]][ch[pre[fa]][1]==fa]=x;
    pre[x]=pre[fa];
    ch[x][kind]=fa;
    pre[fa]=x;
    pushup(fa);
}

int get_pos(int w){         ///找到w所在结点在原序列中的位置
    int low=0,high=cnt-1,mid;
    while(low<=high){
        mid=(low+high)>>1;
        if(s[mid]<=w&&w<=e[mid]) return mid;
        else if(w<s[mid]) high=mid-1;
        else low=mid+1;
    }
}

void Splay(int r,int goal){
    while(pre[r]!=goal){
        if(pre[pre[r]]==goal) Rotate(r,ch[pre[r]][0]==r);
        else{
            int fa=pre[r];
            int kind=(ch[pre[fa]][0]==fa);
            if(ch[fa][kind]==r){
                Rotate(r,!kind);
                Rotate(r,kind);
            }
            else{
                Rotate(fa,kind);
                Rotate(r,kind);
            }
        }
    }
    pushup(r);
    if(!goal) root=r;
}

void Insert(int &r,int fa,int k){///写成递归式为了pushup
    if(!r){
        newnode(r,fa,k);
        return;
    }
    else{
        Insert(ch[r][0],r,k);
        pushup(r);
    }
}

int getmax(int x){
    while(ch[x][1]) x=ch[x][1];
    return x;
}

void del(int x){
    Splay(x,0);
    if(!ch[root][0]){
        root=ch[root][1];
        pre[root]=0;
    }
    else{
        int t=getmax(ch[root][0]);
        Splay(t,root);
        ch[t][1]=ch[root][1];
        pre[ch[root][1]]=t;
        root=t;
        pre[root]=0;
        pushup(root);
    }
}

int get_kth(int r,int k){
    int lst=siz[ch[r][0]];
    if(k<=lst) return get_kth(ch[r][0],k);
    else if(k<=lst+num[r]) return s[key[r]]+(k-lst)-1;
    else return get_kth(ch[r][1],k-lst-num[r]);
}

void Top(int x){
    int k=get_pos(x);
    del(p[k]);
    Insert(root,0,k);
    Splay(tot,0);       ///这是以防该节点会在后面又被用到,这样可以保证各项操作均摊复杂度在log(n)~ORZZZZZZZZ
}

void Query(int x){
    int k=get_pos(x);
    Splay(p[k],0);
    printf("%d\n",1+siz[ch[p[k]][0]]);
}

void Rank(int x){
    printf("%d\n",get_kth(root,x));
}

int main(){
    //freopen("in.txt","r",stdin);
    scanf("%d",&T);
    while(T--){
        printf("Case %d:\n",P++);
        scanf("%d%d",&n,&q);
        init();

        ask[tail++]=0;
        for(int i=0;i<q;i++){
            char arr[10];
            scanf("%s %d",arr,&node[i].x);
            node[i].op=arr[0];
            if(arr[0]=='T'||arr[0]=='Q') ask[tail++]=node[i].x;
        }
        ask[tail++]=n;
        sort(ask,ask+tail);
        for(int i=1;i<tail;i++){
            if(ask[i]!=ask[i-1]){
                if(ask[i]-ask[i-1]>1){
                    s[cnt]=ask[i-1]+1;
                    e[cnt++]=ask[i]-1;
                }
                s[cnt]=e[cnt]=ask[i];
                cnt++;
            }
        }
        build(root,0,cnt-1,0);
        for(int i=0;i<q;i++){
            if(node[i].op=='T') Top(node[i].x);
            else if(node[i].op=='Q') Query(node[i].x);
            else Rank(node[i].x);
        }
    }
    return 0;
}
View Code

 

posted @ 2016-01-15 22:24  Septher  阅读(240)  评论(0编辑  收藏  举报