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?'\n':' ');
        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\n",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\n",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\n",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

 

posted @ 2020-03-03 21:45  DeepJay  阅读(143)  评论(0编辑  收藏  举报