zoukankan      html  css  js  c++  java
  • ZOJ2112 Dynamic Rankings(树套树:树状数组套主席树)

    写法一:

    树状维护区间,内部主席树(动态开点)维护值域;

    树状数组的结点i代表第i个版本的权值线段树并且其是建立在原区间 [ i - lowbit(i) + 1 , i ] ;

    通过树状数组对结点(每个节点都是一个线段树)求和,就能得到一个完整版本的主席树;

    由于树状数组的特殊性,这种写法不需要保存历史版本,直接通过树状向上更新即可;

     但也正是这种写法不能保存历史版本,达不到树链的共用,在内存开销上比较大;

    #include<bits/stdc++.h>
    #pragma GCC optimize(2)
    using namespace std;
    typedef long long LL;
    typedef pair<int,int> pii;
    typedef pair<double,double> pdd;
    const int N=6e4+5;
    const int inf=0x3f3f3f3f;
    const int mod=1e9+7;
    const double eps=1e-9;
    const long double pi=acos(-1.0L);
    #define ls (i<<1)
    #define rs (i<<1|1)
    #define fi first
    #define se second
    #define pb push_back
    #define mk make_pair
    #define mem(a,b) memset(a,b,sizeof(a))
    LL read()
    {
        LL x=0,t=1;
        char ch;
        while(!isdigit(ch=getchar())) if(ch=='-') t=-1;
        while(isdigit(ch)){ x=10*x+ch-'0'; ch=getchar(); }
        return x*t;
    }
    struct node{ int l,r,v; }c[N*40];
    int root[N],t[N],a[N],L[N],R[N],K[N],cmd[N],len,tot,n,m,cnt;
    int qx[N],qy[N],tx,ty;
    void update(int l,int r,int &now,int pos,int d)
    {
        if(now==0) now=++cnt;//没有保存历史版本,如果之前结点存在,也就没有必要再开一个新的节点了;
        c[now].v+=d;
        if(l==r) return ;
        int mid=l+r>>1;
        if(pos<=mid) update(l,mid,c[now].l,pos,d);
        else update(mid+1,r,c[now].r,pos,d);
    }
    inline int lowbit(int x)
    {
        return x&(-x);
    }
    void add(int x,int y)
    {
        for(int i=x;i<=n;i+=lowbit(i)) update(1,len,root[i],a[x],y);
    }
    int query(int l,int r,int k)
    {
        if(l==r) return l;
        int mid=l+r>>1;
        int sum=0;
        for(int i=1;i<=ty;i++) sum+=c[c[qy[i]].l].v;
        for(int i=1;i<=tx;i++) sum-=c[c[qx[i]].l].v;
        if(k<=sum)
        {
            for(int i=1;i<=ty;i++) qy[i]=c[qy[i]].l;
            for(int i=1;i<=tx;i++) qx[i]=c[qx[i]].l;
            return query(l,mid,k);
        }
        else
        {
            for(int i=1;i<=ty;i++) qy[i]=c[qy[i]].r;
            for(int i=1;i<=tx;i++) qx[i]=c[qx[i]].r;
            return query(mid+1,r,k-sum);
        }
    }
    int main()
    {
        int T=read();
        while(T--)
        {
            for(int i=1;i<=cnt;i++) root[i]=c[i].l=c[i].r=c[i].v=0;
            cnt=tx=ty=0;
            tot=n=read(),m=read();
            for(int i=1;i<=n;i++) t[i]=a[i]=read();
            for(int i=1;i<=m;i++)
            {
                char ch[2];
                int x,y,z;
                scanf("%s",ch);
                cmd[i]=ch[0];
                if(ch[0]=='Q') L[i]=read(),R[i]=read(),K[i]=read();
                else L[i]=read(),t[++tot]=R[i]=read();
            }
            sort(t+1,t+tot+1);
            len=unique(t+1,t+tot+1)-t-1;
            //for(int i=1;i<=len;i++) printf("%d%c",t[i],i==len?'
    ':' ');
            for(int i=1;i<=n;i++) a[i]=lower_bound(t+1,t+len+1,a[i])-t;
            for(int i=1;i<=n;i++) add(i,1);
            for(int i=1;i<=m;i++)
            {
                if(cmd[i]=='Q')
                {
                    tx=ty=0;
                    for(int j=L[i]-1;j;j-=lowbit(j)) qx[++tx]=root[j];
                    for(int j=R[i];j;j-=lowbit(j)) qy[++ty]=root[j];
                    printf("%d
    ",t[query(1,len,K[i])]);
                }
                else
                {
                    add(L[i],-1);
                    a[L[i]]=lower_bound(t+1,t+len+1,R[i])-t;
                    add(L[i],1);
                }
            }
        }
        return 0;
    }
    写法一

     写法二:

    在写法一的基础上进行改进:写法一的树状是维护原区间以及修改部分的主席树,(n+m)lognlogn 的内存量相当大,

    而第二种写法中,直接对原区间建立静态的主席树,而树状只维护主席树被修改的部分,原理同方法一,每次询问则同时查询静态主席树和树状的被修改部分,

    如此一来空间复杂度就能优化到 O(nlogn+mlognlogn) 了;

    #include<bits/stdc++.h>
    #pragma GCC optimize(2)
    using namespace std;
    typedef long long LL;
    typedef pair<int,int> pii;
    typedef pair<double,double> pdd;
    const int N=5e4+5;
    const int M=1e4+5;
    const int inf=0x3f3f3f3f;
    const int mod=1e9+7;
    const double eps=1e-9;
    const long double pi=acos(-1.0L);
    #define ls (i<<1)
    #define rs (i<<1|1)
    #define fi first
    #define se second
    #define pb push_back
    #define mk make_pair
    #define mem(a,b) memset(a,b,sizeof(a))
    LL read()
    {
        LL x=0,t=1;
        char ch;
        while(!isdigit(ch=getchar())) if(ch=='-') t=-1;
        while(isdigit(ch)){ x=10*x+ch-'0'; ch=getchar(); }
        return x*t;
    }
    struct node{int l,r,s;}c[N*40];
    int root[N],rooc[N];//root 主席树的根,rooc 树状的根
    int tmp[N+M],a[N],cnt,len,tot,n,m;
    int qx[M],qy[M],tx,ty,L[M],R[M],K[M],cmd[N];
    inline int lowbit(int x)
    {
        return x&(-x);
    }
    void update(int l,int r,int pre,int &now,int pos,int v)
    {
        now=++cnt,c[now]=c[pre],c[now].s+=v; //这里建立静态主席树,需要保存历史版本,如果想在树状上节约空间,可以加一个flag
        if(l==r) return ;
        int mid=l+r>>1;
        if(pos<=mid) update(l,mid,c[pre].l,c[now].l,pos,v);
        else update(mid+1,r,c[pre].r,c[now].r,pos,v);
    }
    void add(int x,int y)
    {
        int d=lower_bound(tmp+1,tmp+len+1,a[x])-tmp;
        for(int i=x;i<=n;i+=lowbit(i)) update(1,len,rooc[i],rooc[i],d,y);
    }
    int query(int l,int r,int x,int y,int k)
    {
        if(l==r) return l;
        int mid=l+r>>1;
        int sum=c[c[y].l].s-c[c[x].l].s;
        for(int i=1;i<=tx;i++) sum-=c[c[qx[i]].l].s;
        for(int i=1;i<=ty;i++) sum+=c[c[qy[i]].l].s;
        if(k<=sum)
        {
            for(int i=1;i<=tx;i++) qx[i]=c[qx[i]].l;
            for(int i=1;i<=ty;i++) qy[i]=c[qy[i]].l;
            return query(l,mid,c[x].l,c[y].l,k);
        }
        else
        {
            for(int i=1;i<=tx;i++) qx[i]=c[qx[i]].r;
            for(int i=1;i<=ty;i++) qy[i]=c[qy[i]].r;
            return query(mid+1,r,c[x].r,c[y].r,k-sum);
        }
    }
    int main()
    {
        int T=read();
        while(T--)
        {
            tot=n=read(),m=read();
            for(int i=1;i<=n;i++) tmp[i]=a[i]=read();
            for(int i=1;i<=m;i++)
            {
                char ch[2];
                scanf("%s",ch);
                cmd[i]=ch[0]=='Q';
                if(cmd[i]==1) L[i]=read(),R[i]=read(),K[i]=read();
                else L[i]=read(),tmp[++tot]=R[i]=read();
            }
            sort(tmp+1,tmp+tot+1);
            len=unique(tmp+1,tmp+tot+1)-tmp-1;
            for(int i=1;i<=n;i++)
            {
                int x=lower_bound(tmp+1,tmp+len+1,a[i])-tmp;
                update(1,len,root[i-1],root[i],x,1);
                //printf("cnt = %d
    ",cnt);
            }
            for(int i=1;i<=m;i++)
            {
                if(cmd[i])
                {
                    tx=ty=0;
                    for(int j=L[i]-1;j;j-=lowbit(j)) qx[++tx]=rooc[j];
                    for(int j=R[i];j;j-=lowbit(j)) qy[++ty]=rooc[j];
                    printf("%d
    ",tmp[query(1,len,root[L[i]-1],root[R[i]],K[i])]);
                }
                else
                {
                    add(L[i],-1);
                    a[L[i]]=R[i];
                    add(L[i],1);
                }
    
            }
            for(int i=1;i<=cnt;i++) root[i]=rooc[i]=0,c[i]=c[0];
            cnt=0;
        }
        return 0;
    }
    写法二

    内存节约版update:(树状不保存历史版本,静态主席树保存历史版本)

    void update(int l,int r,int pre,int &now,int pos,int v,int flag)//flag=1,主席树;flag=0,树状数组
    {
        if(flag||!flag&&now==0) now=++cnt,c[now]=c[pre];
        c[now].s+=v;
        if(l==r) return ;
        int mid=l+r>>1;
        if(pos<=mid) update(l,mid,c[pre].l,c[now].l,pos,v,flag);
        else update(mid+1,r,c[pre].r,c[now].r,pos,v,flag);
    }
    View Code
  • 相关阅读:
    vue 中input的输入限制
    PC端百度地理围栏、绘制工具以及判断当前坐标是否再围栏中
    js判断鼠标点击的是哪个键
    vue过滤器的使用
    3.Mybatis的配置解析
    2.MyBatis的CRUD操作
    4.JVM类加载器深入解析及重要特性剖析
    3.JVM的接口初始化规则与类加载器准备阶段和初始化阶段的重要意义分析
    2.JVM的类加载器
    1.JVM如何学习
  • 原文地址:https://www.cnblogs.com/DeepJay/p/12404840.html
Copyright © 2011-2022 走看看