zoukankan      html  css  js  c++  java
  • 带修主席树 洛谷2617 支持单点更新以及区间kth大查询

    题目链接:https://www.luogu.com.cn/problem/P2617

    参考博客:https://blog.csdn.net/dreaming__ldx/article/details/80872728

    在主席树的基础上实现单点更新也不困难,主要我们要明白主席树的函数性质,也就是一个根节点代表的信息我们可以认为是一段前缀,朴素主席树的T(i)树代表的是区间[1,i]的前缀,这样子单点更新之后就必须更新之后的T(i+1)~T(n)的线段树,这样的话时间复杂度非常高,我们可以利用主席树的函数性质,用树状数组套主席树,树状数组的C[i]点的主席树维护的是[i-lowbit(i)+1,i]区间的插入信息(虽然在根节点都是维护[1,n]区间),也就是T(i)树维护的是lowbit(i)长度的区间。每棵主席树维护的区间不是前缀区间,这个是树状数组套主席树的重点。所以更新的时候我们只要更新树状数组中的logn个结点,而在这每个结点之中我们需要修改一条链上的logn个结点(该结点属于主席树)。最终q次修改+m次查询的时间复杂度是O(q*log^2(n)+mlogn)。注意树状数组是建立在原数组的基础上的,所以树状数组中的索引与原数组的索引相关联,而主席树的索引则是离散值的索引,这与静态主席树相比又复杂了写,体现在add函数中,add函数传入的是实时原数组的索引,所以先修改位置的信息再通过这个位置获得修改的值的大小。

    下面附上自写代码,结构体保存的树的信息,常数比较大:

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 typedef unsigned int ui;
      4 typedef long long ll;
      5 typedef unsigned long long ull;
      6 #define pf printf
      7 #define mem(a,b) memset(a,b,sizeof(a))
      8 #define prime1 1e9+7
      9 #define prime2 1e9+9
     10 #define pi 3.14159265
     11 #define lson l,mid,rt<<1
     12 #define rson mid+1,r,rt<<1|1
     13 #define scand(x) scanf("%llf",&x) 
     14 #define f(i,a,b) for(int i=a;i<=b;i++)
     15 #define scan(a) scanf("%d",&a)
     16 #define mp(a,b) make_pair((a),(b))
     17 #define P pair<int,int>
     18 #define dbg(args) cout<<#args<<":"<<args<<endl;
     19 #define inf 0x3f3f3f3f
     20 const int maxn=1e5+10;
     21 int n,m;
     22 inline int read(){
     23     int ans=0,w=1;
     24     char ch=getchar();
     25     while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
     26     while(isdigit(ch))ans=(ans<<3)+(ans<<1)+ch-'0',ch=getchar();
     27     return ans*w;
     28 }
     29 
     30 struct node{
     31     int l,r,sum;
     32 }t[maxn*405];
     33 struct qu{
     34     int a,b,c;
     35 }q[maxn];
     36 int cnt=0,tot; 
     37 char s[10];
     38 //root中存放主席树的根节点编号,b中存放的所有将会出现在数列中的数 
     39 //b数组乘2是因为可能有1e5的原数据和1e5的更新点 
     40 //a中存放的是数组的实时动态情况,一旦有点修改就会体现在a数组上 
     41 int root[maxn*405],b[maxn<<1],a[maxn],qx[maxn],qy[maxn],ansx,ansy;
     42 //在pre结点的基础上,建立now根结点并且在p位置加上v
     43 //对每棵主席树的插入操作都是一样的,所以带修跟不带修的插入函数一样 
     44 int lowbit(int x){return x&(-x);}
     45 void update(int l,int r,int pre,int &now,int p,int v)
     46 { 
     47      t[++cnt]=t[pre];
     48      now=cnt;
     49      t[now].sum+=v;//(now结点的区间一定是包含p位置的)
     50      if(l==r)return;
     51      int m=l+r>>1;//划分区间,决定向左子树还是右子树更新 
     52      if(p<=m)update(l,m,t[pre].l,t[now].l,p,v);
     53      else update(m+1,r,t[pre].r,t[now].r,p,v); 
     54 }
     55 void add(int x,int v)//给第x个数插上v,这是在树套树的主席树上进行的加操作 
     56 {//对于树状数组i位置上的主席树,根就是root[i] 
     57     int k=lower_bound(b+1,b+tot+1,a[x])-b;
     58     for(int i=x;i<=n;i+=lowbit(i))update(1,tot,root[i],root[i],k,v);//就在第i棵树上进行操作    
     59 }
     60 int query(int l,int r,int k)
     61 {
     62     if(l==r)return l;
     63     int sum=0,m=l+r>>1;
     64     f(i,1,ansy)sum+=t[t[qy[i]].l].sum;
     65     f(i,1,ansx)sum-=t[t[qx[i]].l].sum;//计算[l,m]区间插入的数的数量 
     66     if(k<=sum)
     67     {
     68         f(i,1,ansx)qx[i]=t[qx[i]].l;
     69         f(i,1,ansy)qy[i]=t[qy[i]].l;//同步、更新、递归 
     70         return query(l,m,k); 
     71      } 
     72     else 
     73     {
     74         f(i,1,ansx)qx[i]=t[qx[i]].r;
     75         f(i,1,ansy)qy[i]=t[qy[i]].r;
     76         return query(m+1,r,k-sum);
     77     }
     78 }
     79 int main()
     80 {
     81     //freopen("input.txt","r",stdin);
     82     //freopen("output.txt","w",stdout);
     83     std::ios::sync_with_stdio(false);
     84     n=read(),m=read(),cnt=0,tot=0;
     85     f(i,1,n){
     86         a[i]=b[++tot]=read(); 
     87     }
     88     f(i,1,m)
     89     {
     90         scanf("%s",s);
     91         q[i].a=read(),q[i].b=read();
     92         if(s[0]=='Q')q[i].c=read();
     93         else q[i].c=0,b[++tot]=q[i].b;
     94         //离线处理
     95     }
     96     sort(b+1,b+tot+1);
     97     tot=unique(b+1,b+tot+1)-(b+1);
     98     f(i,1,n)add(i,1);//将第i个数插入主席树 
     99     f(i,1,m)
    100     {
    101         if(q[i].c)
    102         {
    103             ansx=ansy=0;
    104             for(int j=q[i].b;j;j-=lowbit(j))qy[++ansy]=root[j];//把需要查询的点的根节点的编号保存下来 
    105             for(int j=q[i].a-1;j;j-=lowbit(j))qx[++ansx]=root[j];
    106             pf("%d
    ",b[query(1,tot,q[i].c)]);//注意vector中下标从1开始 
    107         }
    108         else 
    109         {
    110             add(q[i].a,-1); 
    111             a[q[i].a]=q[i].b;//将点更新实时地在a上体现 
    112             add(q[i].a,1);
    113         }
    114     }
    115  } 
  • 相关阅读:
    我渴望自由和成功(愿与君共勉)
    gdb使用 和core dump文件调试
    谷歌浏览器快捷键大全
    Android适屏
    BestCoder Round #33
    NAT&amp;Port Forwarding&amp;Port Triggering
    程序员应该学习的书
    java代码调用rtx发送提醒消息
    js实现excel导出
    一个跨界程序员:不务正业的这几年,我如何让自己的收入翻了十倍(转)
  • 原文地址:https://www.cnblogs.com/randy-lo/p/12558430.html
Copyright © 2011-2022 走看看