BZOJ 1901 Dynamic Rankings

摘抄自cydiater大神

可持久化似乎是近几年才开始在OI界流传起来的,很难找到很靠谱的博客。

这篇文章写的很不错。我这里也参考了他的代码写法,写一篇小总结防呆。

算法主体

可持久化,即可以在原有的基础上返回历史版本,类似于一些软件的Ctrl+Z。

主席树,即可持久化线段树,或者说是函数式线段树,即线段树的可持久化。

如何实现可持久化?最简单的,每次有一个操作就把整棵树复制一遍,再在新树上操作。但是显然这个是存在大量优化的空间的。

 

就像上面这个图,这是一颗[1,8]的线段树(几何画板画的不要介意,大概知道什么意思就好了),

如果修改了编号为4的元素,那么红色的部分显然是要修改的,但是黑色的部分并不需要修改。于是每次修改只要在原树上新增O(logN)个节点就行了。这样就做到了即更新了下一版本的信息又不更改上一版本的信息。

是不是觉得有什么不妥?在新版本中怎么实现对黑色部分的访问?

我们之所以产生这样的疑问,是因为在朴素的线段树中,root节点的左儿子和右儿子必定是root×2root×21,即满足二叉树的性质,而在主席树中如果按照上面那样说就很容易理解成转为多叉实现。

其实如果换成类似于平衡树的方式实现就比较好理解了。在每次更新红色部分的时候我们只需要更新左儿子或右儿子其中一个,对于另一个直接指向上一个版本就好了。

 

不带修改的区间第K大

 一个很经典的问题。给定一段序列,要求一个数据结构,支持两个操作。1.修改某个数。2,查询某段区间的第K大。

首先考虑简单点的情况,只支持操作2。

如果只是查最大,就是线段树模板,而第K大就可以套上主席树。

这个长度为N的序列分为N个线段树,第i个线段树存着[1,i]的信息。很显然这里存的信息和普通的序列不一样。

i个线段树存储着区间[1,i]的每个数出现的次数。数值过大?离散化即可。

为什么这样处理?首先考虑给定区间[L,R],要求第K大的数字,首先我们把第L1棵线段树和第R棵线段树取出来,做差。就可以得到在区间[L,R]内每个数出现的次数。

这样子的话提前在每个节点上搞一个sum,就可以用平衡树找rank的方式实现了。

 

但是如果光按照上面所说建N棵线段树肯定不可行,空间上是O(N2)。用上面所说的可持久化优化即可。

即相当于给定一段区间,每次在区间的某一段上+1,然后用历史版本之类的。

 

 带修改的区间第K大

带修改的第K大,或者说动态第K大。

相对来说比较麻烦。首先思考,在求静态第K大时,我们把[L,R]的线段树用TRTL1差分得到。如果把每次操作看作一个值,那么这就是一个前缀和的思想。

而维护前缀和的相关信息最优越的数据结构就是BIT(树状数组)了。

所以先用静态树状数组那一套把所有版本的线段树搞出来。然后再开一颗主席树,用BIT来维护修改。

因为权值线段树是支持加减的,所以把所有的操作和原始版本的做和,就是所求的线段树了,总体复杂度是O(log2N×N)

以上就是介绍,下面放题

1901: Zju2112 Dynamic Rankings

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 8389  Solved: 3479
[Submit][Status][Discuss]

Description

给定一个含有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继续回答上面的问题。

Input

第一行有两个正整数n(1≤n≤10000),m(1≤m≤10000)。
分别表示序列的长度和指令的个数。
第二行有n个数,表示a[1],a[2]……a[n],这些数都小于10^9。
接下来的m行描述每条指令
每行的格式是下面两种格式中的一种。 
Q i j k 或者 C i t 
Q i j k (i,j,k是数字,1≤i≤j≤n, 1≤k≤j-i+1)
表示询问指令,询问a[i],a[i+1]……a[j]中第k小的数。
C i t (1≤i≤n,0≤t≤10^9)表示把a[i]改变成为t
m,n≤10000

Output

 对于每一次询问,你都需要输出他的答案,每一个输出占单独的一行。

Sample Input

5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3

Sample Output

3
6

HINT

Source

用以上的知识就可以解决了

#include <bits/stdc++.h>
#define ll long long
using namespace std;
inline int read(){
    int x=0;int f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int MAXN=5e5+10;
struct Chair_Man_Tree{
    int son[2],sum;
}T[MAXN];
struct node{
    int a,b,k;
    char op;
}Q[MAXN];
int arr[MAXN],fsort[MAXN],root[MAXN],rsum=0,sum=0,n,m,cnt=0,top[2],prevL[MAXN],prevR[MAXN];
namespace zhangenming{
    inline int lowbit(int x) {return x&-x;}
    void init(){
        n=read();m=read();
        sum=n;
        for(int i=1;i<=n;i++){
            fsort[i]=arr[i]=read();
        }
        for(int i=1;i<=m;i++){
            char op[5];
            scanf("%s",op);
            Q[i].op=op[0];Q[i].a=read();Q[i].b=read();
            if(op[0]=='Q') Q[i].k=read();
            else fsort[++sum]=Q[i].b;
        }
        sort(fsort+1,fsort+sum+1);
        rsum=unique(fsort+1,fsort+sum+1)-fsort-1;
    }
    inline int Newnode(int sum,int son0,int son1){
        T[++cnt].sum=sum;T[cnt].son[0]=son0;
        T[cnt].son[1]=son1;return cnt;
    }
    inline void build(int &root,int last,int pos,int leftt,int rightt){
        root=Newnode(T[last].sum+1,T[last].son[0],T[last].son[1]);
        if(leftt==rightt) return;
        int mid=(rightt+leftt)>>1;
        if(pos<=mid) build(T[root].son[0],T[last].son[0],pos,leftt,mid);
        else build(T[root].son[1],T[last].son[1],pos,mid+1,rightt);
    }
    inline int Get(int leftt,int rightt,int rnk){
        if(leftt==rightt) return leftt;
        int summ=0;
        for(int i=1;i<=top[0];i++) summ-=T[T[prevL[i]].son[0]].sum;
        for(int i=1;i<=top[1];i++) summ+=T[T[prevR[i]].son[0]].sum;
        int mid=(leftt+rightt)>>1;
        if(rnk<=summ){
            for(int i=1;i<=top[0];i++)  prevL[i]=T[prevL[i]].son[0];
            for(int i=1;i<=top[1];i++) prevR[i]=T[prevR[i]].son[0];
            return Get(leftt,mid,rnk);
        }
        else{
            for(int i=1;i<=top[0];i++) prevL[i]=T[prevL[i]].son[1];
            for(int i=1;i<=top[1];i++) prevR[i]=T[prevR[i]].son[1];
            return Get(mid+1,rightt,rnk-summ);
        }
    }
    int k=0;
    inline void insert(int leftt,int rightt,int &root,int pos,int val){
        if(!root) root=Newnode(0,0,0);
        T[root].sum+=val;
        if(leftt==rightt) return;
        int mid=(rightt+leftt)>>1;
        if(pos<=mid) insert(leftt,mid,T[root].son[0],pos,val);
        else insert(mid+1,rightt,T[root].son[1],pos,val);
    }
    void solve(){
        for(int i=1;i<=n;i++){
            arr[i]=lower_bound(fsort+1,fsort+rsum+1,arr[i])-fsort;
            build(root[i+n],root[i+n-1],arr[i],1,rsum);
        }
        for(int i=1;i<=m;i++){
            if(Q[i].op=='Q'){
                top[0]=top[1]=0;
                prevL[++top[0]]=root[((Q[i].a-1)==0?0:Q[i].a+n-1)];
                prevR[++top[1]]=root[Q[i].b+n];
                for(int j=Q[i].a-1;j>=1;j-=lowbit(j)){
                    prevL[++top[0]]=root[j];
                }
                for(int j=Q[i].b;j>=1;j-=lowbit(j)){
                    prevR[++top[1]]=root[j];
                }
                printf("%d\n",fsort[Get(1,rsum,Q[i].k)]);
            }
            else{
                for(int j=Q[i].a;j<=n;j+=lowbit(j)){
                    insert(1,rsum,root[j],arr[Q[i].a],-1);
                }
                arr[Q[i].a]=lower_bound(fsort+1,fsort+1+rsum,Q[i].b)-fsort;
                for(int j=Q[i].a;j<=n;j+=lowbit(j)){
                    insert(1,rsum,root[j],arr[Q[i].a],1);
                }
            }
        }
    }
}
int main(){
    //freopen("All.in","r",stdin);
    //freopen("bai.out","w",stdout);
    using namespace zhangenming;
    init();
    solve();
    return 0;
}

  

 

posted @ 2017-11-27 08:17  zhangenming  阅读(...)  评论(...编辑  收藏