• BZOJ3224普通平衡树


    洛谷题面链接
    很早就过了,太久没打了,原本是在noip前用来练emacs的手感的。
    noip炸了,就滚回来更博客了(安排的计数任务刷不动,学不会容斥,打发时间。。。)
    众所周知,splay是个好算法,有着优秀的时间复杂度和更加优(巨)秀(大)的常数,而且我们得写双旋的splay,否则你在luogu得T飞。
    我对平衡树的理解也不深,也只是会使用而已,浅谈啊什么的还是写不出来,我就分析一下每个操作需要注意的吧。

    1.insert(x):

    由于x的范围巨大,你需要用编号表示每个点,每个编号记一个权值

    2.delete(x):

    分多种情况:
    1.只有x一个点,直接删
    2.x只有左儿子,把根定为x的左儿子
    3.x只有右儿子,同理
    4.左右儿子都有,取前驱/后继(假设为y)旋到根节点,此时x节点只有右儿子(想一想为什么),然后直接将y的右儿子定为x的右儿子。

    3.findx(x):

    由于这个询问,你需要把所有权值相同的点都合并成一个点。

    4~6

    真没什么细节了!

    splay代码:

    #include<cstdio>
    int n,size[100001],rt,id,v[100001],ch[100001][2],f[100001],cnt[100001];
    void update(int x){size[x]=size[ch[x][0]]+size[ch[x][1]]+cnt[x];}
    void move(int x,int &k)
    {
        int fa=f[x],faa=f[fa],tmp=(ch[fa][1]==x);
        if(fa==k)k=x;else ch[faa][ch[faa][1]==fa]=x;
        ch[fa][tmp]=ch[x][tmp^1];f[ch[x][tmp^1]]=fa;
        ch[x][tmp^1]=fa;f[fa]=x;f[x]=faa;
        update(fa),update(x);
    }
    void splay(int x,int &k)
    {
        while(x!=k)
        {
            int y=f[x],z=f[y];
            if(y!=k)
                {
                    if((ch[z][0]==y)^(ch[y][0]==x))move(y,k);
                    else move(x,k);
                }
            move(x,k);
        }
    }
    void add(int x)
    {
        if(!rt){size[++id]=1,v[id]=x,rt=id,cnt[id]=1;return ;}
        int now=rt;
        while(1)
        {
            if(x==v[now]){cnt[now]++;update(now);splay(now,rt);return ;}
            else if(x<v[now])
           	{
            	if(!ch[now][0])
                {
                    v[++id]=x;ch[now][0]=id;size[id]=1;cnt[id]=1;
                    f[id]=now;update(now);break;
                }
                now=ch[now][0];
            }
            else 
            {
                if(!ch[now][1])
                {
                    v[++id]=x;ch[now][1]=id;size[id]=1;cnt[id]=1;
                    f[id]=now;update(now);break;
                }
                now=ch[now][1];
            }
        }
        splay(id,rt);
    }
    int findS(int k,int x)
    {
        if(v[k]==x)return k;
        if(v[k]>x)return findS(ch[k][0],x);
        if(v[k]<x)return findS(ch[k][1],x);
    }
    int pre()
    {
        int x=ch[rt][0];
        while(ch[x][1])x=ch[x][1];
        return x;
    }
    int nxt()
    {
        int x=ch[rt][1];
        while(ch[x][0])x=ch[x][0];
        return x;
    }
    int findx(int k,int x)
    {
        if(!k)return 1;
        if(v[k]>x)return findx(ch[k][0],x);
        if(v[k]<x)return size[ch[k][0]]+cnt[k]+findx(ch[k][1],x);
        if(v[k]==x){int d=size[ch[k][0]];splay(k,rt);return d+1;}
    }
    void del(int x)
    {
        int now=findS(rt,x);
        splay(now,rt);if(cnt[now]>1){cnt[now]--;return ;}
        if(!ch[now][0]&&!ch[now][1]){size[now]=cnt[now]=v[now]=0;rt=0;return ;}
        if(ch[now][0]&&!ch[now][1]){int y=ch[now][0];ch[now][0]=f[y]=v[now]=cnt[now]=size[now]=0;rt=y;return ;}
        if(!ch[now][0]&&ch[now][1]){int y=ch[now][1];ch[now][1]=f[y]=v[now]=cnt[now]=size[now]=0;rt=y;return ;}
        int y=pre(),z=ch[now][1];splay(y,rt);f[z]=y;ch[y][1]=z;update(y);
    }
    int find(int k,int x)
    {
        if(x<=size[ch[k][0]])return find(ch[k][0],x);
        if(x>size[ch[k][0]]&&x<=size[ch[k][0]]+cnt[k])return v[k];
        return find(ch[k][1],x-size[ch[k][0]]-cnt[k]);
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1,op,x,y;i<=n;i++)
        {
            scanf("%d",&op);
            if(op==1)scanf("%d",&x),add(x);
            if(op==2)scanf("%d",&x),del(x);
            if(op==3)scanf("%d",&x),printf("%d
    ",findx(rt,x));
            if(op==4)scanf("%d",&x),printf("%d
    ",find(rt,x));
            if(op==5)scanf("%d",&x),add(x),printf("%d
    ",v[pre()]),del(x);
            if(op==6)scanf("%d",&x),add(x),printf("%d
    ",v[nxt()]),del(x);
        }
    }
    

    要知道线段树也能写这个题,权值线段树就好啦
    先离散化,对于每个区间记一下该区间内有多少个数就行了,然后所有操作都可以支持了
    代码:

    #include<cstdio>
    #include<algorithm>
    #include<map>
    using namespace std;
    #define mid ((s[x].l+s[x].r)>>1)
    map<int,int>mp;
    int n,w[100001],a[100001],b[100001],q[100001],tot,ans1,ans2,ans3;
    struct oo{int l,r,v,mx,mn;}s[400001];
    void build(int x,int l,int r)
    {
        s[x].l=l,s[x].r=r,s[x].mn=1e9;
        if(l==r)return ;
        build(x<<1,l,mid),build(x<<1|1,mid+1,r);
    }
    void update(int x)
    {
        s[x].v=s[x<<1].v+s[x<<1|1].v;
        s[x].mx=max(s[x<<1].mx,s[x<<1|1].mx);
        s[x].mn=min(s[x<<1].mn,s[x<<1|1].mn);
    }
    void change(int x,int l,int v)
    {
        if(s[x].l==s[x].r)
            {
                s[x].v+=v;
                if(s[x].v)s[x].mx=s[x].mn=l;
                else s[x].mx=0,s[x].mn=1e9;
                return ;
            }
        if(l<=mid)change(x<<1,l,v);
        else change(x<<1|1,l,v);
        update(x);
    }
    void get(int x,int l,int r)
    {
        if(l>r)return ;
        if(l<=s[x].l&&r>=s[x].r)
            {
                ans1+=s[x].v,ans2=max(ans2,s[x].mx),ans3=min(ans3,s[x].mn);
                return ;
            }
        if(l<=mid)get(x<<1,l,r);
        if(r>mid)get(x<<1|1,l,r);
    }
    int ask(int x,int l)
    {
        if(s[x].l==s[x].r)return s[x].l;
        if(l<=s[x<<1].v)return ask(x<<1,l);
        else return ask(x<<1|1,l-s[x<<1].v);
    }
    int main()
    {
        scanf("%d",&n);int now=0;
        for(int i=1;i<=n;i++){scanf("%d%d",&a[i],&b[i]);if(a[i]!=4)w[++now]=b[i];}
        sort(w+1,w+now+1);
        for(int i=1;i<=now;i++)if(!mp[w[i]])mp[w[i]]=++tot,q[tot]=w[i];
        for(int i=1;i<=n;i++)if(a[i]!=4)b[i]=mp[b[i]];
        build(1,1,tot);
        for(int i=1;i<=n;i++)
            {
                if(a[i]==1)change(1,b[i],1);
                if(a[i]==2)change(1,b[i],-1);
                if(a[i]==3)ans1=ans2=0,ans3=1e9,get(1,1,b[i]-1),printf("%d
    ",ans1+1);
                if(a[i]==4)printf("%d
    ",q[ask(1,b[i])]);
                if(a[i]==5)ans1=ans2=0,ans3=1e9,get(1,1,b[i]-1),printf("%d
    ",q[ans2]);
                if(a[i]==6)ans1=ans2=0,ans3=1e9,get(1,b[i]+1,tot),printf("%d
    ",q[ans3]);
            }
    }
    
    
  • 相关阅读:
    每天一道算法题(13)——使用递归颠倒栈
    每天一道算法题(12)——和为n的连续正数序列或者随机数
    函数模板
    答题总结(1)
    顶点间最短路径长度之探寻算法
    最小生成树
    new与delete,malloc与free
    C++的继承与接口
    笔记13 AOP中After和AfterReturning的区别
    笔记12 注入AspectJ切面
  • 原文地址:https://www.cnblogs.com/lcxer/p/10003445.html
Copyright © 2020-2023  润新知