可修改主席树(树状数组套主席树)

54 篇文章 0 订阅
21 篇文章 0 订阅

作用

普通主席树可以查询区间k小值,但若直接修改,则复杂度极大,而可修改主席树通过树状数组的辅助来修改,大大缩小了时间复杂度,缺点是空间复杂度过大.
修改的时间复杂度为O((logn)^2),空间复杂度为O(n*logn^2).

实现方法

为了减小修改复杂度,可以修改每个区间管理的范围.
传统主席树每个点对应的主席树管理一个前缀,而可修改主席树因为树状数组在处理动态区间和上有优势,每个点管理的范围就是它在树状数组中的管理范围.
具体而言,在加入元素时,对所有在树状数组中包含它的主席树都进行修改,而删除也是同一个道理.
而再查询时与树状数组类似,树状数组的查询是log的点的相加,这里也是一样,log个点一起在主席树上同时同向跳动.
log个点跳log次,故每次的复杂度为log^2.
在建树时,为了能尽可能多的共用节点,可以先建一棵根为0,什么都不管理的主席树,并将所有其他主席树的根节点先初始化为这棵主席树的根节点,最后将元素一一加入即可.
而因为有修改操作,所以在离散化前,要先将问题储存起来,将修改后的变量一起离散化.

代码

(以洛谷 P2617 Dynamic Ranking为例)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<map>
#define C ch=getchar()
#define lb(x) (x&(-x))
#define mid ((l+r)>>1)
#define N 10010
using namespace std;

int m,n,size,tt,num[N],root[N],b[N*2],bb,c[N*2],jia[N],jian[N],ja,jn;
struct Tree
{
    Tree()
    {
        gs=right_son=left_son=0;
    }
    int left,right,left_son,right_son,gs;
} tree[N*400];
struct Cz
{
    char ch;
    int a,b,c;
} cz[N];
map<int,int>id;

void build(int now,int l,int r)
{
    tree[now].gs=0;
    tree[now].left=l;
    tree[now].right=r;
    if(l!=r)
    {
        tree[now].left_son=++tt;
        build(tt,l,mid);
        tree[now].right_son=++tt;
        build(tt,mid+1,r);
    }
}

void ad(int now,int l,int r,int u)
{
    if(!now) return;
    tree[now].gs++;
    tree[now].left=l;
    tree[now].right=r;
    if(l!=r)
    {
        if(u<=mid)
        {
            tree[++tt]=tree[tree[now].left_son];
            tree[now].left_son=tt;
            ad(tt,l,mid,u);
            return;
        }
        tree[++tt]=tree[tree[now].right_son];
        tree[now].right_son=tt;
        ad(tt,mid+1,r,u);
    }
}

inline void add(int u,int v)
{
    int i,j;
    for(i=u; i<=n; i+=lb(i))
    {
        tree[++tt]=tree[root[i]];
        root[i]=tt;
        ad(tt,1,size,v);
    }
}

void de(int now,int u)
{
    tree[now].gs--;
    if(tree[now].left==tree[now].right) return;
    if(u<=tree[tree[now].left_son].right)
    {
        de(tree[now].left_son,u);
        return;
    }
    de(tree[now].right_son,u);
}

inline void del(int u,int v)
{
    int i,j;
    for(i=u; i<=n; i+=lb(i))
    {
        de(root[i],v);
    }
}

int as(int u)
{
    if(tree[jia[1]].left==tree[jia[1]].right) return c[tree[jia[1]].left];
    int i,j,sum=0;
    for(i=1; i<=ja; i++)
    {
        sum+=tree[tree[jia[i]].left_son].gs;
    }
    for(i=1; i<=jn; i++)
    {
        sum-=tree[tree[jian[i]].left_son].gs;
    }
    if(u<=sum)
    {
        for(i=1; i<=ja; i++)
        {
            jia[i]=tree[jia[i]].left_son;
        }
        for(i=1; i<=jn; i++)
        {
            jian[i]=tree[jian[i]].left_son;
        }
        return as(u);
    }
    for(i=1; i<=ja; i++)
    {
        jia[i]=tree[jia[i]].right_son;
    }
    for(i=1; i<=jn; i++)
    {
        jian[i]=tree[jian[i]].right_son;
    }
    return as(u-sum);
}

inline int ask(int u,int v,int w)
{
    int i,j;
    ja=jn=0;
    for(i=u; i; i-=lb(i))
    {
        jia[++ja]=root[i];
    }
    for(i=v; i; i-=lb(i))
    {
        jian[++jn]=root[i];
    }
    return as(w);
}

inline void check(int now)
{
    if(!now) return;
    cout<<tree[now].left<<" "<<tree[now].right<<" "<<tree[now].gs<<endl;
    if(tree[now].left==tree[now].right) return;
    check(tree[now].left_son);
    check(tree[now].right_son);
}

int main()
{
    int i,j;
    char ch;
    cin>>n>>m;
    for(i=1; i<=n; i++)
    {
        scanf("%d",&num[i]);
        b[i]=num[i];
    }
    bb=n;
    for(i=1; i<=m; i++)
    {
        for(C; ch!='C'&&ch!='Q'; C);
        cz[i].ch=ch;
        scanf("%d%d",&cz[i].a,&cz[i].b);
        ch=='Q'?scanf("%d",&cz[i].c):b[++bb]=cz[i].b;
    }
    sort(b+1,b+bb+1);
    for(i=1; i<=bb; i++)
    {
        if(i==1||b[i]!=b[i-1])
        {
            id[b[i]]=++size;
            c[size]=b[i];
        }
    }

    root[0]=++tt;
    build(tt,1,size);
    for(i=1; i<=n; i++)
    {
        tree[++tt]=tree[root[0]];
        root[i]=tt;
    }
    for(i=1; i<=n; i++)
    {
        add(i,id[num[i]]);
    }
//  check(root[1]);
    for(i=1; i<=m; i++)
    {
        if(cz[i].ch=='Q')
        {
            printf("%d\n",ask(cz[i].b,cz[i].a-1,cz[i].c));
        }
        if(cz[i].ch=='C')
        {
            add(cz[i].a,id[cz[i].b]);
            del(cz[i].a,id[num[cz[i].a]]);
            num[cz[i].a]=cz[i].b;
        }
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值