其他分享
首页 > 其他分享> > 动态主席树 可修改主席树 ZOJ 2112

动态主席树 可修改主席树 ZOJ 2112

作者:互联网

题意:

n个数,q个询问 (n<=50000, q<=10000)

Q x y z 代表询问[x, y]区间里的第z小的数

C x y 代表将(从左往右数)第x个数变成y
【样例】
1
5 3
3 2 1 4 7
Q 1 4 3
C 2 6
Q 2 5 3

这里离散化建树过程和静态主席树有一点不同,我们必须把所有询问先存起来并且把改变的数也加入到原序列中再离散化建树,会导致空间复杂度和静态有所区别(之前讲静态的时候提过)。所以这里我们离散化后序列为1 2 3 4 6 7 分别对应原序列的3 2 1 4 7和改变后的6。
之后同静态一样建空树,按原序列前缀建树,相信不用我说了。

在这里插入图片描述

接下来就是重点了,对于题目给出的修改操作,我们新建一批线段树来记录更新,这些线段树以树状数组的思维来维护。
一开始,S[0]、S[1]、S[2]、S[3]、S[4]、S[5] (注意一共有n+1个 即 0到n)(树状数组的每个节点)这些都与T[0]相同(也就是每个节点建了一棵空树)。
对于C 2 6 这个操作,
这个更新我们按树状数组的思想更新,
对于除掉2的影响:
我们要从 index = 2(原序列中第2个数2在离散化后序列中的位置)即S[2]开始更新,并往上index += lowbit(index)直到index大于cnt(不重复元素的个数(包括询问中的)),这里我们会更新S[2]和S[4]。 这次是 每个节点 - 1

边看图边理解(这个图最后应该是在节点5那里减1)

在这里插入图片描述
对于更改为6的影响:
我们还是要从 index = 2(原序列中第2个数2在离散化后序列中的位置)即S[2]开始更新,并往上index += lowbit(index)直到index大于cnt(不重复元素的个数(包括询问中的)),这里我们会更新S[2]和S[4]。 只不过我们这次是 +1
(这个图最后应该是在节点10那里加1)

这样我们查询的时候T[]和静态一样,再按树状数组的思维加上S[]就可算出每个节点对应区间中数的个数,再按静态的思想查询即可。
查询过程
就像一个卡带一样,只有当前需要计算的树它才会留有孔,
不需要的树就没有孔,又因为这些树的结构都是一样的,
所以相当于在一颗树上移动一样,移动到左孩子,对应卡带就会移动

在这里插入图片描述
对于这里的更新树状数组维护的主席树中的一棵树root_S[x],历史版本的树对于这道题根本没有任何用处所以就直接丢弃。我们只需要对原来版本的树进行修改,形成现在的树
旧版本就没用了
看下图中的
root_S[x] = update(root_S[x],1,cnt,res,val);//!!!
这句话

void add(int x,int val)
{
   int res = lower_bound(b+1,b+cnt+1,a[x]) - b;
   while(x <= n)
   {
       root_S[x] = update(root_S[x],1,cnt,res,val);//!!!!
       x += lowbit(x);
   }
}

AC代码如下

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#define inf 200010
struct Node
{
    int l_son,r_son,sum;
}node[inf*10];

struct Query
{
   int l,r,k;
   bool  flag;
}Q[10010];

int root_T[inf],root_S[inf];//T 是原静态树,S 是用树状数组维护的线段树(的根节点)
int a[inf],b[inf];//a 是原数组,b 是排序加长后的数组
int l_rt[inf],r_rt[inf];//分别存储 从左右端点,向左需要遍历的子树的根节点的编号
int n,m,cnt,tot;

inline int build(int l,int r);
int update(int last_father,int l,int r,int x,int val);
void add(int ,int);
int lowbit(int);
int query(int,int,int,int,int,int,int);

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
      char str[5];
      cnt = 0;
      scanf("%d%d",&n,&m);//区间长度为n,m次询问
      for(int i = 1; i <= n; i++)
      {
          scanf("%d",&a[++cnt]),b[cnt] = a[cnt];
         // cout<<Q[i].l<<" "<<Q[i].r<<" "<<Q[i].flag<<endl;
      }

      for(int i = 1; i <= m; i++)
      {
          scanf("%s",str);
          if(str[0] == 'Q')
          {
              scanf("%d%d%d",&Q[i].l,&Q[i].r,&Q[i].k);
              Q[i].flag = true;
          } else
          {
              scanf("%d%d",&Q[i].l,&Q[i].r);
              b[++cnt] = Q[i].r;
              Q[i].flag = false;//一定要赋值为false不同组数据之间有影响
          }
      }
      sort(b+1,b+cnt+1);
 //     cnt = unique(b+1,b+cnt+1) - (b+1);//一共有cnt个不重复元素
      int tmp = unique(b+1,b+cnt+1) - (b+1);
      cnt = tmp;

      tot = 0;
      root_T[0] = build(1,cnt);
       //建立初始静态树 T
        for(int i = 1; i <= n; i++)
        {
            int index = lower_bound(b+1,b+cnt+1,a[i]) - b;
            //  cout<<index<<endl;
            root_T[i] = update(root_T[i-1],1,cnt,index,1);
        }
       //建立初始动态树 S
        for(int i = 1;i <= n; i++) root_S[i] = root_T[0];

      for(int i = 1; i <= m; i++)//处理m次询问
      {
         if(Q[i].flag)//查询操作
         {
           //  cout<<"wc"<<endl;
            for(int j = Q[i].r;  j;j -= lowbit(j)) r_rt[j] = root_S[j];
            for(int j = Q[i].l-1;j;j -= lowbit(j)) l_rt[j] = root_S[j];
            printf("%d\n",b[query(Q[i].l-1,Q[i].r,root_T[Q[i].l-1],root_T[Q[i].r],1,cnt,Q[i].k)]);

         }
         else//修改操作
         {
            add(Q[i].l,-1);
            a[Q[i].l]  = Q[i].r;
            add(Q[i].l,1);
         }
      }
    }

}
int Sum(int x,bool flag)
{
    int res = 0;
    while(x > 0)
    {
        if(flag) res += node[node[r_rt[x]].l_son].sum;
        else res += node[node[l_rt[x]].l_son].sum;
        x -= lowbit(x);
    }
    return res;
}
int query(int s,int e,int ts,int te,int l,int r,int k)
{
    if(l == r) return l;
    int mid = l + r >> 1;
    int res = Sum(e,true) - Sum(s,false) + node[node[te].l_son].sum  - node[node[ts].l_son].sum;
    if(k <= res)
    {
        //i 表示 第 i 颗树 r_rt[i],表示第i颗树下一层计算和的时候要被访问的左儿子的父亲节点的编号
        for(int i = e;i;i -= lowbit(i)) r_rt[i] = node[r_rt[i]].l_son;
        for(int i = s;i;i -= lowbit(i)) l_rt[i] = node[l_rt[i]].l_son;
        return query(s,e,node[ts].l_son,node[te].l_son,l,mid,k);

    }else
    {
        for(int i = e;i;i -= lowbit(i)) r_rt[i] = node[r_rt[i]].r_son;
        for(int i = s;i;i -= lowbit(i)) l_rt[i] = node[l_rt[i]].r_son;
        return query(s,e,node[ts].r_son,node[te].r_son,mid+1,r,k-res);
    }
}
int lowbit(int x)
{
    return x & (-x);
}
//向上维护
void add(int x,int val)
{
   int res = lower_bound(b+1,b+cnt+1,a[x]) - b;
   while(x <= n)
   {
       root_S[x] = update(root_S[x],1,cnt,res,val);
       x += lowbit(x);
   }
}

int update(int last_father,int l,int r,int x,int val)
{
#define  lf last_father

    int rt = ++tot;
    node[rt].l_son = node[lf].l_son;
    node[rt].r_son = node[lf].r_son;
    node[rt].sum = node[lf].sum + val;
    if(l < r)
    {
        int mid = (l+r)>>1;
        if(x <= mid){
            node[rt].l_son = update(node[rt].l_son,l,mid,x,val);
        } else
            node[rt].r_son = update(node[rt].r_son,mid+1,r,x,val);
    }
    return rt;
}

inline int build(int l,int r)
{
    int rt = ++tot;
    if(l < r)
    {
        int mid = (l+r) >> 1;
        node[rt].l_son = build(l,mid);
        node[rt].r_son = build(mid+1,r);
    }
    return rt;
}

https://blog.csdn.net/WilliamSun0122/article/details/77885781
https://blog.csdn.net/qq_37025443/article/details/84929283

标签:node,index,int,ZOJ,son,2112,rt,inf,主席
来源: https://blog.csdn.net/weixin_43912833/article/details/98664353