可持久化数据结构——可持久化线段树

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_34283998/article/details/79320963

可持久化线段树

个人认为大部分数据结构转变为可持久化都是比较简单的,只需要保存历史版本的信息即可。在每次进行更改操作时,先复制的到之前的一个版本,然后对要进行更改的节点,复制一个出来改。并且将它的父亲指向新复制出来的点。

也就是说,我们只是在原来的历史版本上加边加点,并没有重新建一个树。

除此之外我觉得还可以有一个大胆的搞法。历史版本我们可以不用线性的结构保存,我们可以用树状数组来快速实现。这个玩意是可以实现的,但是,实现起来非常恶心。即使如此,我觉得还是有必要的写一写的。

代码是CQBZ-2111(区间查询不带修改操作)
区间查询操作就是有历史版本提供基础的。对于区间[l,r]我们只需要询问第r个和第l-1个历史版本就可以。

    int ncnt,n,m;
    int a[MAXN+5],b[MAXN+5],root[MAXN+5];//root中保存了历史版本
    struct node
    {
        int cnt;
        int ch[2];
    }tree[MAXN*20];

    void add(int &cur,int x,int l,int r)//cur当前节点的编号,x要插入的数,l、r是区间
    {
        tree[++ncnt]=tree[cur];//复制之前的版本
        cur=ncnt;
        tree[cur].cnt++;//插入时只是计数,没有保存权值
        if(l==r)
            return;
        int mid=(l+r)>>1;
        if(x<=mid)
            add(tree[cur].ch[0],x,l,mid);
        else
            add(tree[cur].ch[1],x,mid+1,r);
    }

    int FindKth(int x,int y,int k,int l,int r)
    {
        if(l==r)
            return l;
        int lx=tree[x].ch[0],ly=tree[y].ch[0];
        int t=tree[lx].cnt-tree[ly].cnt;//统计l到r的历史版本中,在此区间内的元素个数
        int mid=(l+r)>>1;
        if(k<=t)
            return FindKth(lx,ly,k,l,mid);
        return FindKth(tree[x].ch[1],tree[y].ch[1],k-t,mid+1,r);//注意这里的k要减去前面数的个数
    }

    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            b[i]=a[i];
        }
        sort(b+1,b+n+1);
        int sum=unique(b+1,b+n+1)-b-1;
        for(int i=1;i<=n;i++)
        {
            root[i]=root[i-1];
            int pos=lower_bound(b+1,b+n+1,a[i])-b;
            add(root[i],pos,1,sum);///此处的pos是离散后的数!
        }
        for(int i=1;i<=m;i++)
        {
            int l,r,k;
            scanf("%d%d%d",&l,&r,&k);
            int pos=FindKth(root[r],root[l-1],k,1,sum);
            printf("%d\n",b[pos]);
        }
    }

另一道题是带有单点修改操作的题CQBZ-2133
这道题中我们就必须用树状数组减少时间复杂度。

    #define MAXN 500000///在内存可以的情况下多开点
    #define MAXQ 10000

    int n,m,ncnt,sum;
    int root[MAXN+5],a[MAXN+5],b[MAXN+5],pos[MAXN+5];
    int L[MAXN+5],R[MAXN+5],cntL,cntR;
    struct node
    {
        int cnt;
        int ch[2];
    }tree[MAXN*20];

    void SegAdd(int &cur,int x,int l,int r,int d)//在线段树中进行加入操作
    {
        if(!cur)
        {
            ncnt++;
            tree[ncnt]=tree[cur];
            cur=ncnt;
        }
        tree[cur].cnt+=d;
        if(l==r)
            return;
        int mid=(l+r)>>1;
        if(x<=mid)
            SegAdd(tree[cur].ch[0],x,l,mid,d);
        else
            SegAdd(tree[cur].ch[1],x,mid+1,r,d);
    }

    void BitAdd(int x,int pos,int d)
    {
        while(x<=n)
        {
            SegAdd(root[x],pos,1,sum,d);
            x+=x&(-x);
        }
    }

    ///对于在线段树上的查询操作,因为首先在树状数组上弄出的历史版本,分别存在L和R里
    ///然后分别统计出在这两个区间里的个数,依次查询
    int SegKth(int l,int r,int k)
    {
        if(l==r)
            return l;
        int cnt=0;
        for(int i=1;i<=cntL;i++)
            cnt-=tree[tree[L[i]].ch[0]].cnt;
        for(int i=1;i<=cntR;i++)
            cnt+=tree[tree[R[i]].ch[0]].cnt;
        int mid=(l+r)>>1;
        if(k<=cnt)
        {
            for(int i=1;i<=cntL;i++)
                L[i]=tree[L[i]].ch[0];
            for(int i=1;i<=cntR;i++)
                R[i]=tree[R[i]].ch[0];
            return SegKth(l,mid,k);
        }
        else
        {
            for(int i=1;i<=cntL;i++)
                L[i]=tree[L[i]].ch[1];
            for(int i=1;i<=cntR;i++)
                R[i]=tree[R[i]].ch[1];
            return SegKth(mid+1,r,k-cnt);
        }
    }

    int BitKth(int x,int y,int k)
    {
        cntL=cntR=0;
        while(x>0)
        {
            L[++cntL]=root[x];
            x-=x&(-x);
        }
        while(y>0)
        {
            R[++cntR]=root[y];
            y-=y&(-y);
        }
        return SegKth(1,sum,k);
    }

    struct Q
    {
        int how;
        int l,r,k;
    }Query[MAXQ+5];

    void solve()
    {
        for(int i=1;i<=n;i++)
        {
            pos[i]=lower_bound(b+1,b+sum+1,a[i])-b;///pos里面存的是a[i]在b中得排名
            BitAdd(i,pos[i],1);
        }
        for(int i=1;i<=m;i++)
        {
            int how=Query[i].how;
            if(how==1)
            {
                int st=Query[i].l,ed=Query[i].r,k=Query[i].k;
                int ans=BitKth(st-1,ed,k);
                printf("%d\n",b[ans]);
            }
            else
            {
                int l=Query[i].l,k=Query[i].k;
                BitAdd(l,pos[l],-1);
                pos[l]=lower_bound(b+1,b+sum+1,k)-b;
                BitAdd(l,pos[l],1);
            }
        }
    }

    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            b[i]=a[i];
        }
        sum=n;
        char s[15];
        for(int i=1;i<=m;i++)
        {
            scanf("%s",s);
            if(s[0]=='Q')
            {
                Query[i].how=1;
                scanf("%d%d%d",&Query[i].l,&Query[i].r,&Query[i].k);
            }
            else
            {
                Query[i].how=2;
                scanf("%d%d",&Query[i].l,&Query[i].k);
                b[++sum]=Query[i].k;
            }
        }
        sort(b+1,b+sum+1);
        sum=unique(b+1,b+sum+1)-b-1;
        solve();
    }

猜你喜欢

转载自blog.csdn.net/qq_34283998/article/details/79320963