Dynamic Ranking(主席树,树套树,树状数组)

洛谷题目传送门

YCB巨佬对此题有详细的讲解。%YCB%请点这里

思路分析

不能套用静态主席树的方法了。因为的\(N\)个线段树相互纠缠,一旦改了一个点,整个主席树统统都要改一遍。。。。。。

话说我真的快要忘了有一种数据结构,能支持单点修改,区间查询,更重要的是,常数优秀的它专门用来高效维护前缀和!!它就是——

!树状数组!

之前静态主席树要保存的每个线段树\([1,i]\),不也是一个庞大的前缀吗?于是,把树状数组套在线段树上,构成支持动态修改的主席树。每个树状数组的节点即为一个线段树的根节点。

举个栗子,维护一个长度为\(5\)的序列,树状数组实际会长成这样——

于是就利用树状数组来维护前缀和了。首先是修改(设修改元素位置为\(i\))。从下标为\(i\)的树状数组节点开始,每次都往后跳(+=lowbit(i)),所有跳到的线段树都改一遍,原值对应区间-1,新值对应区间+1。一共要改\(log\)棵树。

然后是查询。先把\(l-1\)和\(r\)都往前跳(-=lowbit(i)),每次跳到的都记下来。求当前\(size\)的时候,用记下来的\(log\)棵由\(r\)得到的节点左儿子的\(size\)和(就代表\([1,r]\)的\(size\))减去\(log\)棵由\(l-1\)得到的节点左儿子的\(size\)和(就代表\([1,l-1]\)的\(size\))就是\([l,r]\)的\(size\)。往左/右儿子跳的时候也是\(log\)个节点一起跳。

其实还有一个问题,一开始本蒟蒻想不通,就是\(N\)棵线段树已经无法共用内存了,那空间复杂度不会是\(O(N^2\log N)\)吗?

其实没必要担心的。。。。。。

只考虑修改操作,每次有\(log\)棵线段树被挑出来,每个线段树只修改\(log\)个节点,因此程序一趟跑下来,仅有\(N\log^2N\)个节点被访问过,我们只需要动态开点就好了。

下面贴代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define R register int
const int N=10009,M=4000009;//M:开Nlog2的空间
bool op[N];
int L,P,n,a[N],b[N],c[N],d[N],g[N<<1];
int rt[N],lc[M],rc[M],s[M];
int pl,pr,reql[20],reqr[20];
#define G ch=getchar()
#define GO G;while(ch<‘-‘)G
#define in(z) GO;z=ch&15;G;while(ch>‘-‘)z*=10,z+=ch&15,G
inline void update(R p,R v)//修改
{
    R k=lower_bound(g+1,g+L+1,a[p])-g;//先找到离散化后对应值
    for(R i=p;i<=n;i+=i&-i)
    {
        R*t=&rt[i],l=1,r=L,m;
        while(l!=r)
        {
            if(!*t)*t=++P;//动态分配空间
            s[*t]+=v;
            m=(l+r)>>1;
            if(k<=m)r=m,t=&lc[*t];
            else  l=m+1,t=&rc[*t];
        }
        if(!*t)*t=++P;
        s[*t]+=v;
    }
}
inline int ask(R l,R r,R k)
{
    R i,m,sum;
    pl=pr=0;
    for(i=l-1;i;i-=i&-i)
        reql[++pl]=rt[i];
    for(i=r;i;i-=i&-i)
        reqr[++pr]=rt[i];//需要查询的log个线段树全记下来
    l=1;r=L;
    while(l!=r)
    {
        m=(l+r)>>1;sum=0;
        for(i=1;i<=pr;++i)sum+=s[lc[reqr[i]]];
        for(i=1;i<=pl;++i)sum-=s[lc[reql[i]]];//一起加一起减
        if(k<=sum)
        {
            for(i=1;i<=pl;++i)reql[i]=lc[reql[i]];
            for(i=1;i<=pr;++i)reqr[i]=lc[reqr[i]];
            r=m;
        }//一起向同一边儿子跳
        else
        {
            for(i=1;i<=pl;++i)reql[i]=rc[reql[i]];
            for(i=1;i<=pr;++i)reqr[i]=rc[reqr[i]];
            l=m+1;k-=sum;
        }
    }
    return g[l];
}
int main()
{
    register char ch;
    R m,i;
    in(n);in(m);L=n;
    for(i=1;i<=n;++i){in(a[i]);}
    memcpy(g,a,(n+1)<<2);
    for(i=1;i<=m;++i)
    {
        GO;op[i]=ch==‘Q‘;
        in(b[i]);in(c[i]);
        if(op[i]){in(d[i]);}
        else g[++L]=c[i];//变成动态的了,离散化时后面需要修改的值也要考虑进去,所以先把所有操作保存起来
    }
    sort(g+1,g+L+1);
    L=unique(g+1,g+L+1)-g-1;//离散化
    for(i=1;i<=n;++i)update(i,1);//一开始还是每个点都要更新一遍
    for(i=1;i<=m;++i)
    {
        if(op[i])printf("%d\n",ask(b[i],c[i],d[i]));
        else
        {
            update(b[i],-1);//注意被替代的以前那个值要减掉
            a[b[i]]=c[i];
            update(b[i],1);
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/flashhu/p/8324297.html

时间: 2024-10-09 04:01:21

Dynamic Ranking(主席树,树套树,树状数组)的相关文章

【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

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

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

ZOJ 2112 Dynamic Rankings(主席树套树状数组+静态主席树)

题意:给定一个区间,求这个区间第k大的数,支持单点修改. 思路:主席树真是个神奇的东西.........速度很快但是也有一个问题就是占用内存的很大,一般来说支持单点修改的主席树套树状数组空间复杂度为O(n*logn*logn), 如果查询较少的话,可以初始的时候用一颗静态主席树,这样空间复杂度可以降为O(n*logn+q*logn*logn),勉强可以过zoj这道题. 这道题看了好久好久才懂...网上题解一般就几句话.......下面是自己的一些理解. 首先静态主席树这个东西其实比较好懂,就是对

HYSBZ 1901 Dynamic Rankings 树状数组套主席树

ZOJ上面这题内存限制太严格,裸的树套树主席树搞法过不去,BZOJ上面这个放的比较松,可以过. 其实就是利用树状数组维护n颗主席树,然后利用前缀和性质求解第k大. #include <cstdio> #include <cstring> #include <iostream> #include <map> #include <set> #include <vector> #include <string> #include

Luogu Dynamic Ranking (带修改的主席树)

带修改的主席树: 原本的主席树是维护了一个线段树前缀. 那么前缀有没有想到什么东西? 树状数组\(Bits\)是不是很 ...... ? 那么现在,我们用树状数组套主席树,不就可以实现带修改的可持久化了吗. 具体来说 \(T[1]维护rt[1]\) , \(T[2]维护rt[1].rt[2]\) , \(T[3]维护rt[3]\) ...... 就与树状数组是一样的. 那么现在,两个具体的操作: 修改: 修改需要修改\(logN\)棵主席树,将涉及修改节点的\(log\)个主席树先删后加点即可.

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(图解)

BZOJ1901 Zju2112 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继续回答上面的问题. 输入格式 第一行有两个正整数n(1≤n≤10000),m(1≤m≤10000). 分别表示序列的长度和指令的个数. 第二行有n个数,表示a[1],a[2]--a[n],这些数都小于10^9. 接下来的m

[bzoj1901][Zju2112]Dynamic Rankings_主席树

Dynamic Rankings bzoj-1901 Zju-2112 题目大意:给定一个n个数的序列,m个操作,支持:单点修改:查询区间k小值. 注释:$1\le n,m\le 10^4$. 想法:如果这个教树套树的话,我也没办法. 其实就是借用了树状数组的思想,我们在这里叫它...阉割树状数组把. 具体地,主席树每个节点维护的仍然是前缀权值线段树. 修改的时候将修改的点二进制lowbit分解.在分解的节点的权值线段树上直接修改. 查询时我们将所有区间(左端点-1)都二进制lowbit分解,然

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

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