浅谈树状数组套主席树

话说主席树还没写就先写这一篇了\(qwq\)

回顾一下主席树的实现过程:类似查分思想,将线段树的每次修改看做函数式以支持可持久化。因为这样的线段树是可减的。

那么我们维护信息的时候,就要维护每一次新形成的信息。但是我们可以根据前一个信息的基础上进行改动,而不必要去再建一棵树。

所以总而言之,是前缀和的思想。

那么,当需要修改的时候,怎么做呢?

考虑普通的区间操作,当做单点修改的时候,一般用树状数组,线段树和分块。最好实现的就是树状数组。

考虑用树状数组来维护主席树的信息。

树状数组中维护了每一次加入一个数的新形成的主席树的信息。

对于修改一个值,考虑树状数组的单点修改,若修改点\(p\),则把一路上的\(p+lowbit(p)\)全部修改即可,这样是\(O(log)\)的,主席树修改是\(O(log)\)的,即总复杂度\((\log^2{n})\).

查询的时候,我们可以用树状数组提前将第\(r\)棵树和第\(l-1\)棵树的信息预处理出来,是\(log\)的,然后在树上二分,跳左右子树的时候,这\(log\)个信息也一起跳。大概复杂度也是\(O(\log^2{n})\)的。

总时间复杂度:\(O(n\log^2{n})\)

代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+10;
int n,m,a[MAXN],u[MAXN],x[MAXN];
int l[MAXN],r[MAXN],k[MAXN],cur;
int cur1,cur2,q1[MAXN],q2[MAXN],v[MAXN];
char op[MAXN];
set<int>ST;
map<int,int>mp;
struct SGT{
    int cur,rt[MAXN<<2],sum[MAXN<<5],lc[MAXN<<5],rc[MAXN<<5];
    void build(int &o){o=++cur;}
    void print(int o,int l,int r){
        if(!o)return;
        if(l==r&&sum[o])printf("%d",l);
        int mid=l+r>>1;
        print(lc[o],l,mid);
        print(rc[o],mid+1,r);
    }
    void update(int &o,int l,int r,int x,int v){
        if(!o)o=++cur;
        sum[o]+=v;
        if(l==r)return ;
        int mid=l+r>>1;
        if(x<=mid)update(lc[o],l,mid,x,v);
        else update(rc[o],mid+1,r,x,v);
    }
}st;
inline int lbt(int x){return x&(-x);}
void upd(int o,int x,int v){
    for(;o<=n;o+=lbt(o))st.update(st.rt[o],1,n,x,v);
}
void gtv(int o,int *A,int &p){
    p=0;
    //A数组维护了树状数组o所控制的所有根信息
    for(;o;o-=lbt(o))A[++p]=st.rt[o];
}
int query(int l,int r,int k){
    if(l==r)return l;
    int mid=l+r>>1,siz=0;
    for(int i=1;i<=cur1;++i)siz+=st.sum[st.lc[q1[i]]];
    //q1是r树的信息
    for(int i=1;i<=cur2;++i)siz-=st.sum[st.lc[q2[i]]];
    //q2是l-1树的信息,通过gtv处理
    //siz代表信息的区间[l,r]信息
    if(siz>=k){
        for(int i=1;i<=cur1;++i)q1[i]=st.lc[q1[i]];
        for(int i=1;i<=cur2;++i)q2[i]=st.lc[q2[i]];
        return query(l,mid,k);//q1q2一样改,走左子树
    }
    else{
        for(int i=1;i<=cur1;++i)q1[i]=st.rc[q1[i]];
        for(int i=1;i<=cur2;++i)q2[i]=st.rc[q2[i]];
        return query(mid+1,r,k-siz);
    }
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)scanf("%d",a+i),ST.insert(a[i]);
    for(int i=1;i<=m;++i){
        scanf(" %c",op+i);
        if(op[i]=='C')scanf("%d%d",u+i,x+i),ST.insert(x[i]);
        else scanf("%d%d%d",l+i,r+i,k+i);
    }
    for(set<int>::iterator it=ST.begin();it!=ST.end();++it)
        mp[*it]=++cur,v[cur]=*it;//unique
    for(int i=1;i<=n;++i)a[i]=mp[a[i]];
    for(int i=1;i<=m;++i)if(op[i]=='C')x[i]=mp[x[i]];
    n+=m;
    for(int i=1;i<=n;++i)upd(i,a[i],1);
    for(int i=1;i<=m;++i){
        if(op[i]=='C'){
            upd(u[i],a[u[i]],-1);
            upd(u[i],x[i],1);
            a[u[i]]=x[i];
        }
        else{
            gtv(r[i],q1,cur1);//预处理
            gtv(l[i]-1,q2,cur2);//预处理
            printf("%d\n",v[query(1,n,k[i])]);
        }
    }
    return 0;
}

代码中的\(set\)是用来去重的,\(map\)是用来离散化的。

注意询问的字符输入,不要漏了前面的空格。

原文地址:https://www.cnblogs.com/h-lka/p/12294168.html

时间: 2024-11-07 09:32:16

浅谈树状数组套主席树的相关文章

BZOJ 3196 Tyvj 1730 二逼平衡树 ——树状数组套主席树

[题目分析] 听说是树套树.(雾) 怒写树状数组套主席树,然后就Rank1了.23333 单点修改,区间查询+k大数查询=树状数组套主席树. [代码] #include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <set> #include <map> #include <string> #include <alg

[BZOJ 3196] 二逼平衡树 树状数组套主席树

3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 3357  Solved: 1326[Submit][Status][Discuss] Description 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:1.查询k在区间内的排名2.查询区间内排名为k的值3.修改某一位值上的数值4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)5.查询k在区间内的后继(后继定义为

【BZOJ1901】Dynamic Rankings,树状数组套主席树

Time:2016.05.09 Author:xiaoyimi 转载注明出处谢谢 传送门(权限) 题面 1901: Zju2112 Dynamic Rankings Time Limit: 10 Sec Memory Limit: 128 MB Submit: 6678 Solved: 2777 [Submit][Status][Discuss] Description 给定一个含有n个数的序列a[1],a[2],a[3]--a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a

[COGS257]动态排名系统 树状数组套主席树

257. 动态排名系统 时间限制:5 s   内存限制:512 MB [问题描述]给定一个长度为N的已知序列A[i](1<=i<=N),要求维护这个序列,能够支持以下两种操作:1.查询A[i],A[i+1],A[i+2],...,A[j](1<=i<=j<=N)中,升序排列后排名第k的数.2.修改A[i]的值为j.所谓排名第k,指一些数按照升序排列后,第k位的数.例如序列{6,1,9,6,6},排名第3的数是6,排名第5的数是9.[输入格式]第一行包含一个整数D(0<=

关于树状数组套主席树的一些博客

哇仿佛磕了几百年啊; 废话不多说,以下是帮助很大的一些blog: ZOJ 2112 Dynamic Rankings (动态第k大,树状数组套主席树) 主席树全纪录(这个很好) 主席树乱讲(没啥关系,不过有些题目可以刷??) 随笔分类 - 数据结构---主席树(同上) 原文地址:https://www.cnblogs.com/wwtt/p/10099695.html

【树套树】【树状数组套主席树】

这是你顾第一次写[树套树]!!!!!!!! [原题] 求区间第k小元素,区间可修改 [正解] 如果没有修改的话,就直接写搞个主席树利用前缀和加加减减一下就好了.但是多了个修改,修改以为着从当前修改节点k到往后n-k个树顶所代表的树全部都要修改,这是一件非常操蛋的事情.回想起多年前学数据结构初步的时候,区间批量修改无非就是树状数组or线段树.故我们借用树状数组的轮廓来构建主席树的各树顶. 对树状数组每个节点,我们都当成是主席树的树顶,改树顶所涵盖的区间与树状数组该节点意义相同. [查询]查询区间[

【树状数组套主席树】带修改区间K大数

P2617 Dynamic Rankings 题目描述给定一个含有n个数的序列a[1],a[2],a[3]……a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a[i+2]……a[j]中第k小的数是多少(1≤k≤j-i+1),并且,你可以改变一些a[i]的值,改变后,程序还能针对改变后的a继续回答上面的问题.你需要编一个这样的程序,从输入文件中读入序列a,然后读入一系列的指令,包括询问指令和修改指令. 对于每一个询问指令,你必须输出正确的回答. 输入输出格式输入格

zoj 2112 Dynamic Rankings(树状数组套主席树)

题意:对于一段区间,每次求[l,r]的第k大,存在单点修改操作: 思路: 学习主席树参考: http://blog.csdn.net/wjf_wzzc/article/details/24560117(各种形式) http://blog.csdn.net/bossup/article/details/31921235(推荐) http://blog.csdn.net/xiaofengcanyuexj/article/details/25553521?utm_source=tuicool(图解)

【BZOJ 1901】【Zju 2112】 Dynamic Rankings 动态K值 树状数组套主席树模板题

达神题解传送门:http://blog.csdn.net/dad3zz/article/details/50638360 说一下我对这个模板的理解: 看到这个方法很容易不知所措,因为动态K值需要套树状数组,而我一开始根本不知道该怎么套,, 学习吧,,, 然后我自己脑补如果不套会如何?后来想到是查询O(logn),修改是O(nlogn),很明显修改的复杂度太大了,为了降低修改的复杂度,我们只得套上树状数组来维护前缀和使它的n的复杂度降低为logn,从而修改的复杂度变为O(log2n).但因为我们套