BZOJ3224 普通平衡树

BZOJ3224: Tyvj 1728 普通平衡树
Description

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
1. 插入x数
2. 删除x数(若有多个相同的数,因只删除一个)
3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
4. 查询排名为x的数
5. 求x的前驱(前驱定义为小于x,且最大的数)
6. 求x的后继(后继定义为大于x,且最小的数)
Input

第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)
Output

对于操作3,4,5,6每行输出一个数,表示对应答案
Sample Input
10

1 106465

4 1

1 317721

1 460929

1 644985

1 84185

1 89851

6 81968

1 492737

5 493598

Sample Output
106465

84185

492737

HINT

1.n的数据范围:n<=100000

2.每个数的数据范围:[-2e9,2e9]


写了Splay、Treap和替罪羊树。然而并不觉得后面两个好写或者比Splay快啊。

Splay:

#include <bits/stdc++.h>
#define N 100010
#define INF 10000010
#define mid ((l+r)>>1)
#define Lc cur->ch[0]
#define Rc cur->ch[1]
#define rep(i,l,r) for (int i=l;i<=r;i++)
using namespace std;
int n;
struct node{
    int x,num,size;
    node *ch[2],*fa;
}t[N],*root,*tail=t;

template <class Aqua>
inline void read(Aqua &s){
    s=0; Aqua f=1; char c=getchar();
    while (!isdigit(c)) {if (c=='-') f=-1; c=getchar();}
    while (isdigit(c)) s=s*10+c-'0',c=getchar();
    s*=f;
}

inline node *newnode(int x,int d){
    node *cur=++tail;
    cur->x=x; cur->num=cur->size=d;
    cur->ch[0]=cur->ch[1]=cur->fa=t;
    return cur;
}

void pre(){
    root=newnode(-INF,1);
    root->ch[1]=newnode(INF,1);
    root->ch[1]->fa=root;
    root->size=2;
    t->x=t->num=t->size=0;
    t->ch[0]=t->ch[1]=t->fa=t;
}

inline node *Min(node *a,node *b){
    return (a->x<b->x)?a:b;
}

inline node *Max(node *a,node *b){
    return (a->x<b->x)?b:a;
}

inline int get(node *cur){
    return (cur->fa->ch[0]==cur)?0:1;
}

inline void update(node *cur){
    cur->size=Lc->size+Rc->size+cur->num;
}

void rotate(node *cur){
    node *fa=cur->fa; int x=get(cur);
    cur->fa=fa->fa; fa->fa->ch[get(fa)]=cur;
    fa->fa=cur; fa->ch[x]=cur->ch[x^1];
    fa->ch[x]->fa=fa; cur->ch[x^1]=fa;
    update(fa),update(cur);
}

void splay(node *cur,node *&to){
    for (node *end=to->fa;cur->fa!=end;rotate(cur))
        if (cur->fa->fa!=end)
            rotate((get(cur)^get(cur->fa))?cur:cur->fa);
    to=cur;
}

void insert(int x){
    node *fa,*cur;
    for (cur=root;cur!=t;cur=(cur->x>x)?Lc:Rc){
        cur->size++; fa=cur;
        if (cur->x==x){
            cur->num++; break;
        }
    }
    if (cur==t){
        cur=newnode(x,1); cur->fa=fa;
        fa->ch[(x<fa->x)?0:1]=cur;
    }
    splay(cur,root);
}

node *find(node *cur,int x){
    return (cur->x==x)?cur:((cur->x>x)?find(Lc,x):find(Rc,x));
}

int qrank(node *cur){
    splay(cur,root);
    return Lc->size+1;
}

node *select(node *cur,int k){
    return (k<=Lc->size)?select(Lc,k):((k<=Lc->size+cur->num)?cur:select(Rc,k-Lc->size-cur->num));
}

node *pre(node *cur,int x){
    return (cur==t)?t+1:((cur->x>=x)?pre(Lc,x):Max(cur,pre(Rc,x)));
}

node *nxt(node *cur,int x){
    return (cur==t)?t+2:((cur->x<=x)?nxt(Rc,x):Min(cur,nxt(Lc,x)));
}

int main(){
    read(n);
    pre();
    int opt,x; node *tmp;
    rep(i,1,n){
        read(opt),read(x);
        if (opt==1)
            insert(x);
        if (opt==2){
            tmp=find(root,x);
            if (tmp->num!=1)
                tmp->num--,splay(tmp,root);
            else{
                splay(pre(root,x),root);
                splay(nxt(root,x),root->ch[1]);
                root->ch[1]->ch[0]=t;
                update(root->ch[1]),update(root);
            }
        }
        if (opt==3){
            printf("%d\n",qrank(tmp=find(root,x))-1);
            splay(tmp,root);
        }
        if (opt==4){
            printf("%d\n",(tmp=select(root,x+1))->x);
            splay(tmp,root);
        }
        if (opt==5){
            printf("%d\n",(tmp=pre(root,x))->x);
            splay(tmp,root);
        }
        if (opt==6){
            printf("%d\n",(tmp=nxt(root,x))->x);
            splay(tmp,root);
        }
    }
    return 0;
}

Treap:

#include <bits/stdc++.h>
#define N 100010
#define INF INT_MAX
using namespace std;
int n;
struct tree{
    tree *ch[2],*fa;
    int p,v,size;
}t[N],*tail=t,*root=t;

template <class Aqua>
inline void read(Aqua &s){
    s=0; Aqua f=1; char c=getchar();
    while (!isdigit(c)) {if (c=='-') f=-1; c=getchar();}
    while (isdigit(c)) s=s*10+c-'0',c=getchar();
    s*=f;
}

inline int getfa(tree *cur){
    return (cur->fa->ch[0]==cur?0:1);
}

inline void update(tree *cur){
    cur->size=cur->ch[0]->size+cur->ch[1]->size+1;
}

inline void rotate(tree *cur){
    int x=getfa(cur); tree *fa=cur->fa;
    fa->fa->ch[getfa(fa)]=cur;
    cur->fa=fa->fa;
    cur->ch[x^1]->fa=fa;
    fa->ch[x]=cur->ch[x^1];
    fa->fa=cur;
    cur->ch[x^1]=fa;
    update(fa); update(cur);
}

tree *insert(tree *cur,int d,int p,tree *fa){
    if (cur==t){
        cur=++tail; cur->size=1;
        cur->v=d; cur->p=p;
        cur->fa=fa;
        cur->ch[0]=cur->ch[1]=t;
        fa->ch[d<fa->v?0:1]=cur;
        return cur;
    }
    int x=d<cur->v?0:1;
    cur->ch[x]=insert(cur->ch[x],d,p,cur);
    if (cur->ch[x]->p<cur->p)
        rotate(cur=cur->ch[x]);
    update(cur);
    return cur;
}

tree *del(tree *cur,int d){
    if (cur->v==d){
        if (cur->size==1)
            return t;
        else{
            int x=cur->ch[0]->p<=cur->ch[1]->p?0:1;
            rotate(cur=cur->ch[x]);
            cur->ch[x^1]=del(cur->ch[x^1],d);
            update(cur);
            return cur;
        }
    }
    int x=d<cur->v?0:1;
    cur->ch[x]=del(cur->ch[x],d);
    update(cur);
    return cur;
}

int getrank(tree *cur,int d){
    return (cur==t?1:(cur->v>=d?getrank(cur->ch[0],d):cur->ch[0]->size+1+getrank(cur->ch[1],d)));
}

tree *select(tree *cur,int d){
    return (d<=cur->ch[0]->size?select(cur->ch[0],d):(d>cur->ch[0]->size+1?select(cur->ch[1],d-cur->ch[0]->size-1):cur));
}

int main(){
    srand(20010823);
    read(n);
    int op,x;
    root=insert(root,-INF,rand(),t);
    root=insert(root,INF,rand(),t);
    t->size=0; t->p=INF;
    for (int i=1;i<=n;i++){
        read(op),read(x);
        if (op==1)
            root=insert(root,x,rand(),t);
        if (op==2)
            root=del(root,x);
        if (op==3)
            printf("%d\n",getrank(root,x)-1);
        if (op==4)
            printf("%d\n",select(root,x+1)->v);
        if (op==5)
            printf("%d\n",select(root,getrank(root,x)-1)->v);
        if (op==6)
            printf("%d\n",select(root,getrank(root,x+1))->v);
    }
    return 0;
}

Scapegoat Tree:

#include <bits/stdc++.h>
#define N 100010
#define INF 2000000010
#define alpha 0.65
using namespace std;
int n,cnt;
struct tree{
    tree *fa,*ch[2];
    int size,num,x;
}t[N],*q[N],*root=t+1,*node=t,*tail=t+2;

template <class Aqua>
inline void read(Aqua &s){
    s=0; Aqua f=1; char c=getchar();
    while (!isdigit(c)) {if (c=='-') f=-1; c=getchar();}
    while (isdigit(c)) s=s*10+c-'0',c=getchar();
    s*=f;
}

void pre(){
    root->x=-INF; (t+2)->x=INF;
    root->ch[1]=t+2; (t+2)->fa=root;
    root->ch[0]=root->fa=(t+2)->ch[0]=(t+2)->ch[1]=t;
    (t+2)->size=root->num=(t+2)->num=1; root->size=2;
}

inline tree *newnode(int x){
    (++tail)->x=x;
    tail->size=tail->num=1;
    tail->ch[0]=tail->ch[1]=t;
    return tail;
}

inline bool check(tree *cur){
    return (max(cur->ch[0]->size,cur->ch[1]->size)>=(double)cur->size*alpha);
}

void ins(tree *cur,int x){
    cur->size++;
    if (cur->x==x && !cur->num){
        cur->num++;
        return;
    }
    int k=(x<cur->x?0:1);
    if (cur->ch[k]==t){
        cur->ch[k]=newnode(x);
        cur->ch[k]->fa=cur; 
    }
    else
        ins(cur->ch[k],x);
    if (check(cur)) node=cur;
}

void del(tree *cur,int x){
    cur->size--;
    if (cur->num && cur->ch[0]->size+1==x){
        cur->num--;
        if (check(cur)) node=cur;
        return;
    }
    if (cur->ch[0]->size+cur->num>=x)
        del(cur->ch[0],x);
    else
        del(cur->ch[1],x-cur->ch[0]->size-cur->num);
    if (check(cur)) node=cur;
}

int rank(tree *cur,int x){
    return (cur==t?1:(cur->x>=x?rank(cur->ch[0],x):rank(cur->ch[1],x)+cur->ch[0]->size+cur->num));
}

int select(tree *cur,int x){
    return (cur->ch[0]->size>=x?select(cur->ch[0],x):(cur->ch[0]->size+cur->num<x?select(cur->ch[1],x-cur->ch[0]->size-cur->num):cur->x));
}

void dfs(tree *cur){
    if (cur==t) return;
    dfs(cur->ch[0]);
    if (cur->num)
        q[++cnt]=cur;
    dfs(cur->ch[1]);
}

void build(tree *&cur,int l,int r,tree *fa){
    if (l>r){
        cur=t; return;
    }
    int mid=l+r>>1;
    cur=q[mid];
    cur->fa=fa;
    build(cur->ch[0],l,mid-1,cur);
    build(cur->ch[1],mid+1,r,cur);
    cur->size=cur->ch[0]->size+cur->ch[1]->size+1;
}

void rebuild(tree *cur,int x){
    cnt=0; dfs(cur);
    tree *fa=cur->fa;
    build((fa==t?root:(fa->ch[0]==cur?fa->ch[0]:fa->ch[1])),1,cnt,fa);
}

int main(){
    read(n);
    pre();
    int opt,x;
    for (int i=1;i<=n;node=t,i++){
        read(opt),read(x);
        if (opt==1)
            ins(root,x);
        if (opt==2)
            del(root,rank(root,x));
        if (opt==3)
            printf("%d\n",rank(root,x)-1);
        if (opt==4)
            printf("%d\n",select(root,x+1));
        if (opt==5)
            printf("%d\n",select(root,rank(root,x)-1));
        if (opt==6)
            printf("%d\n",select(root,rank(root,x+1)));
        if (node!=t)
            rebuild(node,i);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述 有一个 $n$ 个点的棋盘,每个点上有一个数字 $a_i$,你需要从 $(1,1)$ 走到 $(n,n)$,每次只能往右或往下走,每个格子只能经过一次,路径上的数字和为 $S$。定义一个点 $(x,y)$ 的权值为 $a_x+a_y$,求所有满足条件的路径中,所有点的权值和的最小值。 输入格式 第一行一个整数 $n$。 接下来 $n$ 行,每行 $n$ 个整数,表示棋盘上每个点的数字。 输出格式 输出一个整数,表示所有满足条件的路径中,所有点的权值和的最小值。 数据范围 $1\leq n\leq 300$ 输入样例 3 1 2 3 4 5 6 7 8 9 输出样例 25 算法1 (树形dp) $O(n^3)$ 我们可以先将所有点的权值求出来,然后将其看作是一个有权值的图,问题就转化为了在这个图中求从 $(1,1)$ 到 $(n,n)$ 的所有路径中,所有点的权值和的最小值。 我们可以使用树形dp来解决这个问题,具体来说,我们可以将这个图看作是一棵树,每个点的父节点是它的前驱或者后继,然后我们从根节点开始,依次向下遍历,对于每个节点,我们可以考虑它的两个儿子,如果它的两个儿子都被遍历过了,那么我们就可以计算出从它的左儿子到它的右儿子的路径中,所有点的权值和的最小值,然后再将这个值加上当前节点的权值,就可以得到从根节点到当前节点的路径中,所有点的权值和的最小值。 时间复杂度 树形dp的时间复杂度是 $O(n^3)$。 C++ 代码 算法2 (动态规划) $O(n^3)$ 我们可以使用动态规划来解决这个问题,具体来说,我们可以定义 $f(i,j,s)$ 表示从 $(1,1)$ 到 $(i,j)$ 的所有路径中,所有点的权值和为 $s$ 的最小值,那么我们就可以得到如下的状态转移方程: $$ f(i,j,s)=\min\{f(i-1,j,s-a_{i,j}),f(i,j-1,s-a_{i,j})\} $$ 其中 $a_{i,j}$ 表示点 $(i,j)$ 的权值。 时间复杂度 动态规划的时间复杂度是 $O(n^3)$。 C++ 代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值