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; }