主席树详解 zoj 2112 (单点更新)+ poj 2104 区间第k大

来源:互联网 发布:win8电脑屏幕录制软件 编辑:程序博客网 时间:2024/05/07 23:10
解决问题:单点更新,区间询问第k大的数是多少
缺点:容易MLE
复杂度:n*logn*logS (n个数,离散化后有S个不同的数)
1.树状数组套线段树:树状数组表示区间,理论上树状数组上每个元素对应一棵线段树,线段树rank区间在[l,r]的数已经有多少个,所有的线段树的结构都是一样的
2.离线操作,离散化,排序:离散化知道整个操作过程要有哪些数,排序后知道他们的rank是多少,rank值相当于一张表,当要在x位置改成u,在树状数组x,x+=x&(-x).....位置上更新线段树上rank[u]的位置,要查询[l,r]上第k小的数,得到此时第k小的数的rank,查rank表,知道这个数是多少
3.保持历史版本:由于每棵树的结构一样,所以可以共用节点。理论上有n棵线段树,但实际最开始为0,每次更新根据需要在原有的树上加入新节点
4.查询具有f[l,r]=f[1,r]-f[1,l-1]的性质
zoj 2112
#include <iostream>#include <cstdio>#include <algorithm>using namespace std;#define MAXN 50050#define MAXM 10500class CMT{private:    struct Node    {        int sz;        Node *ch[2];        Node()        {            sz=0;ch[0]=ch[1]=NULL;        }        Node(int _sz,Node *ll,Node *rr)        {            sz=_sz;ch[0]=ll;ch[1]=rr;        }        int gets(){ return this?sz:0;} // 如果为空就返回0        int getls() { return this?ch[0]->gets():0;}        int getrs() { return this?ch[1]->gets():0;}    }pool[(MAXN+MAXM)*40],*a[MAXN],*b[MAXN],*c[40],*d[40]; // pool是总共节点数,a,b是n个数,    int used,amount,left,right;//原来有n个数,避免一个个插入时n*logn*logS的复杂度,直接插入n*logS,在a数组上操作//之后单点更新在b数组上操作,n*logn*logS,b是树状数组模式的操作,b上每个元素对应一棵线段树的根    Node *malloc(Node *root,int w)    {        //if(pool[used]==NULL)            //pool[used]=new Node(); //new出来后放在内存池,避免TLE        Node *now=&pool[used++];        if(root==NULL)//b数组操作时        {            now->sz=w;            now->ch[0]=NULL;//每次复用时要初始化            now->ch[1]=NULL;        }        else//a数组操作时        {            now->sz=root->sz+w;            now->ch[0]=root->ch[0];            now->ch[1]=root->ch[1];        }        return now;    }    Node *insert(Node *root,int x,int w,int l,int r)//a数组上的插入    {        Node *now=malloc(root,w);        if(l!=r)        {            if(x<=(l+r)/2) now->ch[0]=insert(now->ch[0],x,w,l,l+r>>1);            else now->ch[1]=insert(now->ch[1],x,w,(l+r>>1)+1,r);        }        return now;    }    Node *change(Node *root,int x,int w,int l,int r)//b数组上的单点更新    {        if(root==NULL) root=malloc(NULL,0);        if(l!=r)        {            if(x<=(l+r>>1)) root->ch[0]=change(root->ch[0],x,w,l,l+r>>1);            else root->ch[1]=change(root->ch[1],x,w,(l+r>>1)+1,r);        }        root->sz+=w;        return root;    }    int query(int k,int lc,int ld,int l,int r)    {        if(l==r) return l;        int sum=0;        for(int i=0;i<lc;++i)            if(c[i])            sum-=c[i]->getls();        for(int i=0;i<ld;++i)            if(d[i])            sum+=d[i]->getls();        if(k<=sum)        {            for(int i=0;i<lc;++i)                if(c[i])                c[i]=c[i]->ch[0];            for(int i=0;i<ld;++i)                if(d[i])                d[i]=d[i]->ch[0];            return query(k,lc,ld,l,l+r>>1);        }        else // 如果k大于在左儿子上的个数,说明要找的数在右儿子上        {            for(int i=0;i<lc;++i)                if(c[i])                c[i]=c[i]->ch[1];            for(int i=0;i<ld;++i)                if(d[i])                d[i]=d[i]->ch[1];            return query(k-sum,lc,ld,(l+r>>1)+1,r);        }    }public:    void clear(int n,int l,int r)//l,r是整棵线段树的左右区间,根据全部数排序时是从0还是1开始排,决定l是0还是1    {        used=0;        amount=n;        left=l;        right=r;        for(int i=0;i<=n;++i)// a,b都是1-n的            a[i]=b[i]=NULL;    }    void insert(int i,int x,int w)    {        a[i]=insert(a[i-1],x,w,left,right);//a[i]是1-i的叠加    }    int query(int l,int r,int k)//要询问[l+1,r]的结果,即f(r)-f(l)    {        int lc=0,ld=0;        c[lc++]=a[l];        d[ld++]=a[r];        for(int x=l;x;x-=x&(-x))            c[lc++]=b[x];        for(int x=r;x;x-=x&(-x))            d[ld++]=b[x];        return query(k,lc,ld,left,right);    }    void change(int i,int x,int w)    {        while(i<=amount)        {            b[i]=change(b[i],x,w,left,right);            i+=i&(-i);        }    }}cmt;struct CMD{    int x,y,k;    char ch;}cmd[MAXM];int a[MAXN],b[MAXM+MAXN],tot,n,m;int main (){    int T;scanf("%d",&T);    while(T--)    {        scanf("%d%d",&n,&m);        tot=0;        for(int i=0;i<n;++i)        {            scanf("%d",&a[i]);            b[tot++]=a[i];        }        for(int i=0;i<m;++i)        {            scanf(" %c",&cmd[i].ch);            if(cmd[i].ch=='Q')                scanf("%d%d%d",&cmd[i].x,&cmd[i].y,&cmd[i].k);            else {                    scanf("%d%d",&cmd[i].x,&cmd[i].k);                b[tot++]=cmd[i].k;            }        }        sort(b,b+tot);        int cnt=1;        for(int i=1;i<tot;++i)            if(b[i]!=b[i-1])            b[cnt++]=b[i];        tot=cnt;        cmt.clear(n,0,tot-1);        for(int i=0;i<n;++i)        {            int j=lower_bound(b,b+tot,a[i])-b;            cmt.insert(i+1,j,1);// i+1是a数组上更新的位置,j是对应的线段树更新的位置        }        for(int i=0;i<m;++i)        {            if(cmd[i].ch=='Q')            {                printf("%d\n",b[cmt.query(cmd[i].x-1,cmd[i].y,cmd[i].k)]); //f[l,r]=f[1,r]-f[1,l-1]            }            else            {                int j=lower_bound(b,b+tot,a[cmd[i].x-1])-b;                cmt.change(cmd[i].x,j,-1);                a[cmd[i].x-1]=cmd[i].k;                j=lower_bound(b,b+tot,a[cmd[i].x-1])-b;                cmt.change(cmd[i].x,j,1);            }        }    }    return 0;}



poj2104
#include <iostream>#include <cstdio>#include <algorithm>using namespace std;#define MAXN 105050#define MAXM 10050class CMT{private:    struct Node    {        int sz;        Node *ch[2];        int gets(){ return this?sz:0;} // 如果为空就返回0        int getls() { return this?ch[0]->gets():0;}        int getrs() { return this?ch[1]->gets():0;}    }pool[MAXN*50],*a[MAXN];// pool是总共节点数,a,b是n个数,    int used,amount,left,right;//原来有n个数,避免一个个插入时n*logn*logS的复杂度,直接插入n*logS,在a数组上操作//之后单点更新在b数组上操作,n*logn*logS,b是树状数组模式的操作,b上每个元素对应一棵线段树的根    Node *malloc(Node *root,int w)    {        Node *now=&pool[used++];        if(root==NULL)//b数组操作时        {            now->sz=w;            now->ch[0]=NULL;//每次复用时要初始化            now->ch[1]=NULL;        }        else//a数组操作时        {            now->sz=root->sz+w;            now->ch[0]=root->ch[0];            now->ch[1]=root->ch[1];        }        return now;    }    Node *insert(Node *root,int x,int w,int l,int r)//a数组上的插入    {        Node *now=malloc(root,w);        if(l!=r)        {            if(x<=(l+r)/2) now->ch[0]=insert(now->ch[0],x,w,l,l+r>>1);            else now->ch[1]=insert(now->ch[1],x,w,(l+r>>1)+1,r);        }        return now;    }    int query(Node *lq,Node *rq,int k,int l,int r)    {        if(l==r) return l;        int sum=rq->getls()-lq->getls();        if(k<=sum)        {            if(lq) lq=lq->ch[0];            if(rq) rq=rq->ch[0];            return query(lq,rq,k,l,l+r>>1);        }        else        {            if(lq) lq=lq->ch[1];            if(rq) rq=rq->ch[1];            return query(lq,rq,k-sum,(l+r>>1)+1,r);        }    }public:    void clear(int n,int l,int r)//l,r是整棵线段树的左右区间,根据全部数排序时是从0还是1开始排,决定l是0还是1    {        used=0;        amount=n;        left=l;        right=r;        for(int i=0;i<=n;++i)// a,b都是1-n的            a[i]=NULL;    }    void insert(int i,int x,int w)    {        a[i]=insert(a[i-1],x,w,left,right);//a[i]是1-i的叠加    }    int query(int l,int r,int k)    {        return query(a[l],a[r],k,left,right);    }}cmt;struct CMD{    int x,y,k;}cmd[MAXM];int a[MAXN],b[MAXN],tot,n,m;int main (){        scanf("%d%d",&n,&m);        tot=0;        for(int i=0;i<n;++i)        {            scanf("%d",&a[i]);            b[++tot]=a[i];        }        for(int i=0;i<m;++i)        {            scanf("%d%d%d",&cmd[i].x,&cmd[i].y,&cmd[i].k);        }        sort(b+1,b+1+tot);        int cnt=1;        for(int i=2;i<=tot;++i)            if(b[i]!=b[i-1])            b[++cnt]=b[i];        tot=cnt;        cmt.clear(n,1,tot);        for(int i=0;i<n;++i)        {            int j=lower_bound(b+1,b+1+tot,a[i])-b;            cmt.insert(i+1,j,1);// i+1是a数组上更新的位置,j是对应的线段树更新的位置        }        for(int i=0;i<m;++i)        {                printf("%d\n",b[cmt.query(cmd[i].x-1,cmd[i].y,cmd[i].k)]); //f[l,r]=f[1,r]-f[1,l-1]        }    return 0;}


原创粉丝点击