数据结构:树套树-树状数组套主席树

BZOJ1901

这就是所谓的支持修改的主席树,其实我不太认为这是一个树套树,外面的那层树不够显然

int n,m,top,tot,sz;
int v[10005],num[20005],A[10005],B[10005],K[10005],flag[10005],hash[20005],root[10005];
int sum[2200005],lch[2200005],rch[2200005];
int L[35],R[35],a,b;

这个题大概能看出来离线操作的一些端倪来

top是个游标来指示num数组的,num把题目中涉及到的值都存起来了,然后再用hash离散化处理

这里的离散化是因为主席树是一个完全二叉树,是对权值的建树,如果不把1,2,4,6,7变成1,2,3,4,5势必要浪费大量的空间

A,B,K和flag把所有的查询工作存起来了,这也是主席树的特性之一,如果是那种强制在线的题,只能常规树套树了

root是用来存每一个树状数组节点(C数组)所引出的主席树的根节点,sum,lch和rch应该是线段树家族的常客了

这里补充一下主席树的基本知识,首先对要进行操作的序列的前缀建权值树。每当询问[l, r]的时候,只要用[1, r]的树减去[1, l-1]的树,然后就可以查找第K小了
然后对每个前缀建树的时候,只需要新增Logn个点,连到前一个前缀所对应的线段树上,这样就仿佛建了n棵线段树一样
查询操作的话,我们只要找到对应这个区间的线段树,然后再去按照正常的主席树查询就可以了

其实查询操作就是正规的主席树查询而已,只不过我们把初始节点也执行了更新操作,这样原树为空,所有的东西就都依托于树状数组了

这里可以联想一下二维树状数组那里的,我可以维护原始数据的前缀和,只用树状数组维护delta,也可以索性全部update

如果是索性全部update的话,查询的时候就要先查树状数组找到若干树状数组节点,再查询这些节点对应的主席树了

int query(int l,int r,int k)
{
    if(l==r) return l;
    int i,suml=0,sumr=0;
    for(i=1;i<=a;i++) suml+=sum[lch[L[i]]];
    for(i=1;i<=b;i++) sumr+=sum[lch[R[i]]];
    int mid=(l+r)>>1;
    if(sumr-suml>=k)
    {
        for(i=1;i<=a;i++) L[i]=lch[L[i]];
        for(i=1;i<=b;i++) R[i]=lch[R[i]];
        return query(l,mid,k);
    }
    else
    {
        for(i=1;i<=a;i++) L[i]=rch[L[i]];
        for(i=1;i<=b;i++) R[i]=rch[R[i]];
        return query(mid+1,r,k-(sumr-suml));
    }
}

最后查的结果是经过了离散化的,别忘了还原

更新操作就必须完全依赖树状数组了,更新都是先删再加,不管是删还是加,其根本的原理都是执行了一次insert,这样就会在原来的基础上多出logn个节点

这里我们就想到题目为啥要把所有的东西放在一起了,就是为了一块儿建树,更新的东西在查之前已经准备好了

再说说可持久化,可持久化是支持查询历史版本的,因为我的原始数据和更新数据都是一起建的,那么我更新之前和更新之后的东西,只要找到对应历史版本的树根查询就可以了

然后看一下更新操作:

void update(int last,int l,int r,int &rt,int w,int x)
{
    rt=++sz;
    sum[rt]=sum[last]+x;lch[rt]=lch[last];rch[rt]=rch[last];
    if(l==r) return;
    int mid=(l+r)>>1;
    if(w<=mid) update(lch[last],l,mid,lch[rt],w,x);
    else update(rch[last],mid+1,r,rch[rt],w,x);
}

纯粹的主席树update,只不过需要调用logn次,毕竟有logn个树状数组点嘛

如果我们把历史版本的root覆盖了,当然就是真的覆盖了,就像本题一样,因为题目也没问历史版本怎么怎么样了

总的来说,主席树这个东西,值得去细细研究一番

而且通过这个题,离散化的板子有啦,哈哈

 

  1 #include<cstdio>
  2 #include<algorithm>
  3 using namespace std;
  4 int n,m,top,tot,sz;
  5 int v[10005],num[20005],A[10005],B[10005],K[10005],flag[10005],hash[20005],root[10005];
  6 int sum[2200005],lch[2200005],rch[2200005];
  7 int L[35],R[35],a,b;
  8 int lowbit(int x)
  9 {
 10     return x&(-x);    
 11 }
 12 int find(int x)
 13 {
 14     int l=1,r=tot,mid;
 15     while(l<=r)
 16     {
 17         int mid=(l+r)>>1;
 18         if(hash[mid]<x) l=mid+1;
 19         else r=mid-1;
 20     }
 21     return l;
 22 }
 23 void update(int last,int l,int r,int &rt,int w,int x)
 24 {
 25     rt=++sz;
 26     sum[rt]=sum[last]+x;lch[rt]=lch[last];rch[rt]=rch[last];
 27     if(l==r) return;
 28     int mid=(l+r)>>1;
 29     if(w<=mid) update(lch[last],l,mid,lch[rt],w,x);
 30     else update(rch[last],mid+1,r,rch[rt],w,x);
 31 }
 32 int query(int l,int r,int k)
 33 {
 34     if(l==r) return l;
 35     int i,suml=0,sumr=0;
 36     for(i=1;i<=a;i++) suml+=sum[lch[L[i]]];
 37     for(i=1;i<=b;i++) sumr+=sum[lch[R[i]]];
 38     int mid=(l+r)>>1;
 39     if(sumr-suml>=k)
 40     {
 41         for(i=1;i<=a;i++) L[i]=lch[L[i]];
 42         for(i=1;i<=b;i++) R[i]=lch[R[i]];
 43         return query(l,mid,k);
 44     }
 45     else
 46     {
 47         for(i=1;i<=a;i++) L[i]=rch[L[i]];
 48         for(i=1;i<=b;i++) R[i]=rch[R[i]];
 49         return query(mid+1,r,k-(sumr-suml));
 50     }
 51 }
 52 //Q i j k (i,j,k是数字,1≤i≤j≤n, 1≤k≤j-i+1)表示询问指令,询问a[i],a[i+1]……a[j]中第k小的数
 53 //C i t (1≤i≤n,0≤t≤10^9)表示把a[i]改变成为t
 54 int main()
 55 {
 56     char s[3];
 57     scanf("%d%d",&n,&m);
 58     for(int i=1;i<=n;i++)
 59     {
 60         scanf("%d",&v[i]);
 61         num[++top]=v[i];
 62     }
 63     for(int i=1;i<=m;i++)
 64     {
 65         scanf("%s",s);
 66         scanf("%d%d",&A[i],&B[i]);
 67         if(s[0]=='Q') {scanf("%d",&K[i]);flag[i]=1;}
 68         else num[++top]=B[i];
 69     }
 70     sort(num+1,num+top+1);
 71     hash[++tot]=num[1];
 72     for(int i=2;i<=top;i++)
 73     if(num[i]!=num[i-1])
 74         hash[++tot]=num[i];  //把查询离散化,相同的查询合并
 75     for(int i=1;i<=n;i++)
 76     {
 77         int t=find(v[i]);  //找到v[i]离散化之后的下标 
 78         for(int j=i;j<=n;j+=lowbit(j))
 79             update(root[j],1,tot,root[j],t,1);
 80     }
 81     for(int i=1;i<=m;i++)
 82     if(flag[i])
 83     {
 84         a=0;b=0;A[i]--;  //找到区间所对应的树状数组的根节点? 
 85         for(int j=A[i];j>0;j-=lowbit(j))
 86             L[++a]=root[j];
 87         for(int j=B[i];j>0;j-=lowbit(j))
 88             R[++b]=root[j];
 89         printf("%d\n",hash[query(1,tot,K[i])]); 
 90     }
 91     else
 92     {
 93         int t=find(v[A[i]]);
 94         for(int j=A[i];j<=n;j+=lowbit(j))
 95             update(root[j],1,tot,root[j],t,-1);
 96         v[A[i]]=B[i];
 97         t=find(B[i]);
 98         for(int j=A[i];j<=n;j+=lowbit(j))
 99             update(root[j],1,tot,root[j],t,1);
100     }
101     return 0;
102 }

 

posted @ 2018-07-26 16:53  静听风吟。  阅读(729)  评论(0编辑  收藏  举报