• [模板] 带修改主席树


    > 我就算去学整体二分,CDQ分治,也不学树套树。~~树套树真好用~~。 ## 带修改主席树 我相信来看带修改主席树的应该自认为对主席树很是了解,如果还没深刻理解请不要看继续看下去了,因为那样就算学了也无法深刻理解。 首先我们来分析一下主席树,它是权值线段树+离散化+再加动态开点,使空间复杂度和时间复杂度都降到了$nlogn$。那么如果带一个单点修改该怎么办?我们就想到了支持单点修改,区间查询的数据结构树状数组和线段树,但由于线段树常数又大,码量又大于树状数组。所以在这里我们用树状数组套主席树。我们先来看一下树状数组的图。 ![](https://images2018.cnblogs.com/blog/1296339/201808/1296339-20180814082005685-1048819318.png)

    这时容易发现,只要把树状数组上的每一个节点换成一颗主席树就行了,
    单点修改,直接按照树状数组的修改就行了。
    如果不理解的话,就想象一下树状数组的每一个节点挂了一颗主席树。

    时间复杂度

    主席树查询复杂度为(nlogn),树状数组时间复杂度为(logn),所以时间复杂度为(n{logn}^2)

    空间复杂度

    主席树空间复杂度为(nlogn),每个节点最多存入(logn)个树状数组(因为每次加lowbit(i)),
    由于我们是在线开点,所以空间复杂度为(n{logn}^2)
    代码如下:

    // luogu-judger-enable-o2
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int a[100010],hash[101000],tot,root[201000],cnt,n,m,tt,qll[200100],qrr[20000];
    int q1,q2,id[201000],b[201000];
    struct TREE
    {
        int ln,rn,zhi;
    }t[10010000];
    struct NODE
    {
        int l,r,k,flag;
    }q[100100];
    int lowbit(int x) {return (x)&(-x);}
    void gai(int &node,int l,int r,int hs,int v)
    {
        if(!node) node=++tot;
        t[node].zhi+=v;
        if(l==r) return;
        int mid=(l+r)/2;
        if(hs<=mid) gai(t[node].ln,l,mid,hs,v);
        else gai(t[node].rn,mid+1,r,hs,v);
    }
    void add(int p,int v)
    {
        hash[p]=lower_bound(a+1,a+1+tt,hash[p])-a;
        //cout<<hash[p]<<endl;
        for(int i=p;i<=n;i+=lowbit(i)) gai(root[i],1,tt,hash[p],v);
    }
    char s[2];
    int SUM()
    {
        int ans1=0,ans2=0;
        for(int i=1;i<=q1;i++) ans1+=t[t[qrr[i]].ln].zhi;
        for(int i=1;i<=q2;i++) ans2+=t[t[qll[i]].ln].zhi;
        return ans1-ans2;
    
    }
    int cha(int qr,int ql,int l,int r,int k)
    {
        q1=0,q2=0;
        for(int i=qr;i>=1;i-=lowbit(i)) qrr[++q1]=root[i];
        for(int i=ql;i>=1;i-=lowbit(i)) qll[++q2]=root[i];
        while(l<r)
        {
            int lsiz=SUM(),mid=(l+r)/2;
            if(k<=lsiz) 
            {
                for(int i=1;i<=q1;i++) qrr[i]=t[qrr[i]].ln;
                for(int i=1;i<=q2;i++) qll[i]=t[qll[i]].ln;
                r=mid;
            }
            else
            {
                for(int i=1;i<=q1;i++) qrr[i]=t[qrr[i]].rn;
                for(int i=1;i<=q2;i++) qll[i]=t[qll[i]].rn;
                l=mid+1;k-=lsiz;
            }
        }
        return l;
    }
    int main()
    {
        int x,y,z;
        cin>>n>>m;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);b[i]=a[i];
            hash[++cnt]=a[i];
        }
        for(int i=1;i<=m;i++)
        {
            scanf("%s",s);
            if(s[0]=='Q')
            scanf("%d%d%d",&q[i].l,&q[i].r,&q[i].k),q[i].flag=1;
            else
            {
                scanf("%d%d",&q[i].l,&q[i].r);
                a[++cnt]=q[i].r;hash[cnt]=a[cnt];
            }
        }
        sort(a+1,a+1+cnt);
        tt=unique(a+1,a+1+cnt)-a-1;
        for(int i=1;i<=n;i++)
        add(i,1);
        for(int i=1;i<=m;i++)
        {
            if(q[i].flag==1)
            printf("%d
    ",a[cha(q[i].r,q[i].l-1,1,tt,q[i].k)]);
            else
            {
                hash[q[i].l]=b[q[i].l];
                add(q[i].l,-1);
                hash[q[i].l]=q[i].r;
                b[q[i].l]=q[i].r;
                add(q[i].l,1);
            }
        }
    }
    
  • 相关阅读:
    委托和事件
    解决kalilinux:“下列签名无效: KEYEXPIRED 1425567400"
    【c# 学习笔记】委托的使用
    【c# 学习笔记】c#委托是什么
    【c# 学习笔记】面向对象编程的应用
    【c# 学习笔记】接口与抽象类
    【C# 开发技巧】番外篇故事-我是一个线程
    【物联网硬件安全】二、固件分析-固件逆向
    【物联网硬件安全】二、固件分析-固件提取
    【物联网硬件安全】一、硬件分析-电路分析
  • 原文地址:https://www.cnblogs.com/kzj-pwq/p/9468411.html
Copyright © 2020-2023  润新知