zoukankan      html  css  js  c++  java
  • 【bzoj3065】: 带插入区间K小值 详解——替罪羊套函数式线段树

     不得不说,做过最爽的树套树————

    由于有了区间操作,我们很容易把区间看成一棵平衡树,对他进行插入,那么外面一层就是平衡树了,这就与我们之前所见到的不同了。我们之前所见到的大多数是线段树套平衡树而此题中插入时坐标会改变即必须对其找到合适的顺序,而线段树无疑是不支持动态插入的,他维护的是一个静态区间(因为插入一个点整个区间的二分结构可能全部改变,这用他就无法通过区间二分来维护信息了)。所以说我们必须来进行平衡树套线段树,那么平衡树就需要维护区间,而线段树就需要维护权值,之前我们的树套树,在外边一个静态区间线段树里面每个节点都有这个点dfs序所对应区间里的数的有序排列,在这里我们用平衡树来维护区间,那么对于里面的点,YY一下,也是对应于父子关系的一段区间,但我们无法将里面的点有序化,但是我们得到了一个可视且可用的一段取值区间这样我们就可以用动态开点来维护权值信息,一般的平衡树像Splay Treap都需要旋转操作而每次旋转里面的线段树维护的权值信息都会随着父子关系的改变而改变,每次都需要新的线段树(合并也行),常数大到飞起(Treap插入乘个log,Spaly插入乘个log查询log2,虽然看起来没那么差甚至根由更优,但是想一想常数),那么替罪羊就要来当大佬了(表示不会无旋Treap),这样只有每次重构才会将重建(合并)线段树,大约有㏒₂n次重建。

     空间复杂度:替罪羊n;线段树如果不修改  ∑(i=1 ~log2n)n*[log2n-∑(j=1~log2n-i+1)[(2i-1)/2i]],平均每个点152个,

                     如果修改的话2*n+∑(i=1~log₂n-1)[17-∑(j=1~log₂n-j+2)[(2i-1)/2i]]*n*2,平均每个点280个

     这样的话我们就需要回收空间了,不仅是在重建的时候,也必须得是在修改的时候(别问我为什么这么说QAQ),把他维持到152个以内

    时间复杂度:一开始的点用O(n+nlog₂²n),每次插入O(log₂²n),修改O(log₂²n),查询:每一层被选中的点和根(不要忘了我们是在分平衡树千万不要忘了中间那个点)的个数不超过四个(这个我不会理性的证,我先感性的证一下:1.你把这棵树建出来,开始在同一深度找,找到五个的时候怎么也找不到2.这么吊的数据结构查询怎么也得是log级别的啊),所以就是O(log₂n+log₂3n),总重建O(log₂n*(n+∑(i=1 ~log2n)n*[log2n-∑(j=1~log2n-i+1)[(2i-1)/2i]]+n*log²n))大约10*log²n*n     

                 所以总时间复杂度:O(q*log₂3n)估大一点200000*173也就是109由于每个点10s也就行了

    然而人傻自带大常数........

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #define MAXN 140100
    #define inf 70000
    using namespace std;
    inline int read()
    {
       int s=0;
       char ch=getchar();
       while(ch<'0'||ch>'9')ch=getchar();
       while(ch>='0'&&ch<='9')
       {
          s=(s<<3)+(s<<1)+ch-48;
          ch=getchar();
       }
       return s;
    }
    const double alpha=0.75;
    struct Tree
    {
       Tree *ch[2];
       int size,l,r,mid;
    }*null,*stack[120*MAXN],pool[120*MAXN],*now[MAXN];
    int Now[MAXN],num,Num;
    int list[MAXN],len;
    int top;
    struct ScapeGoat_Tree
    {
       ScapeGoat_Tree *ch[2];
       int size,key;
       Tree *root;
       bool bad()
       {
         return size*alpha+5<ch[0]->size||size*alpha+5<ch[1]->size;
       }
       void pushup()
       {
         size=ch[0]->size+ch[1]->size+1;
       }
    }*Null,*root,*lst[MAXN],node[MAXN];
    int sz;
    inline void Init()
    {
       null=pool;
       null->size=null->l=null->r=null->mid=0;
       null->ch[1]=null->ch[0]=null;
       for(int i=1;i<(100*MAXN);i++)stack[++top]=pool+i;
       Null=node;
       Null->size=Null->key=0;
       Null->ch[1]=Null->ch[0]=Null;
       Null->root=null;
       root=Null;
    }
    inline Tree *New(int l,int r)
    {
       Tree *p=stack[top--];
       p->l=l;
       p->r=r;
       p->mid=(l+r)>>1;
       p->size=0;
       p->ch[0]=p->ch[1]=null;
       return p;
    }
    inline ScapeGoat_Tree *New(int key)
    {
       ScapeGoat_Tree *p=&node[++sz];
       p->ch[0]=p->ch[1]=Null;
       p->size=1;
       p->key=key;
       p->root=null;
       return p;
    }
    void recovery(Tree *p)
    {
       if(p==null)return;
       recovery(p->ch[0]);
       stack[++top]=p;
       recovery(p->ch[1]);
    }
    void travel(ScapeGoat_Tree *p)
    {
       if(p==Null)return;
       travel(p->ch[0]);
       lst[++len]=p;
       list[len]=p->key;
       recovery(p->root);
       p->root=null;
       travel(p->ch[1]);
    }
    void ins(Tree *&p,int key,int l,int r)
    {
       if(p==null)p=New(l,r);
       p->size++;
       if(p->l==p->r)return;
       if(p->mid<key) ins(p->ch[1],key,p->mid+1,r);
       else ins(p->ch[0],key,l,p->mid);
    }
    ScapeGoat_Tree *divide(int l,int r)
    {
       if(l>r)return Null;
       int mid=(l+r)>>1;
       for(int i=l;i<=r;i++)ins(lst[mid]->root,list[i],0,inf);
       lst[mid]->ch[0]=divide(l,mid-1);
       lst[mid]->ch[1]=divide(mid+1,r);
       lst[mid]->pushup();
       return lst[mid];
    }
    void Ins(ScapeGoat_Tree *p,int key,int pos)
    {
       ins(p->root,key,0,inf);
       if(p->ch[0]->size+1==pos)
       {
          p->key=key;
          return;
       }
       if(p->ch[0]->size>=pos)Ins(p->ch[0],key,pos);
       else Ins(p->ch[1],key,pos-p->ch[0]->size-1);
    }
    inline void rebuild(ScapeGoat_Tree *&p)
    {
       len=0;
       travel(p);
       p=divide(1,len);
    }
    ScapeGoat_Tree **insert(ScapeGoat_Tree *&p,int key,int pos)
    {
       if(p==Null)
       {
         p=New(key);
         ins(p->root,key,0,inf);
         return &Null;
       }
       p->size++;
       ins(p->root,key,0,inf);
       ScapeGoat_Tree **ret;
       if(p->ch[0]->size+1>=pos)ret=insert(p->ch[0],key,pos);
       else ret=insert(p->ch[1],key,pos-p->ch[0]->size-1);
       if(p->bad())ret=&p;
       return ret;
    }
    inline void Insert(int key,int pos)
    {
       ScapeGoat_Tree **p=insert(root,key,pos);
       if(*p!=Null)rebuild(*p);
    }
    inline int find(int pos)
    {
       ScapeGoat_Tree *p=root;
       while(1)
        if(p->ch[0]->size+1==pos)return p->key;
        else if(p->ch[0]->size>=pos)p=p->ch[0];
        else pos-=p->ch[0]->size+1,p=p->ch[1];
    }
    void del(Tree *&p,int key)
    {
       p->size--;
       if(p->l==p->r)
       {
          if(p->size==0)
          {
             stack[++top]=p;
             p=null;
          }
          return;
       }
       del(p->ch[p->mid<key],key);
       if(p->size==0)
       {
         stack[++top]=p;
         p=null;
       }
    }
    void Del(ScapeGoat_Tree *p,int pos,int key)
    {
       del(p->root,key);
       if(p->ch[0]->size+1==pos)return;
       if(p->ch[0]->size>=pos)Del(p->ch[0],pos,key);
       else Del(p->ch[1],pos-p->ch[0]->size-1,key);
    }
    int rank(Tree *p,int key)
    {
       if(p==null)return 0;
       if(p->l==p->r)return 0;
       if(key<=p->mid)return rank(p->ch[0],key);
       else return p->ch[0]->size+rank(p->ch[1],key);
    }
    void pre(ScapeGoat_Tree *p,int l,int r)
    {
       if(p==Null)return;
       if(l<=1&&p->size<=r)
       {
          now[++num]=p->root; 
          return;
       }
       if(l<=p->ch[0]->size)pre(p->ch[0],l,r);
       if(l<=p->ch[0]->size+1&&r>=p->ch[0]->size+1)Now[++Num]=p->key;
       if(p->ch[0]->size+1<r)pre(p->ch[1],l-p->ch[0]->size-1,r-p->ch[0]->size-1);
    }
    inline int Rank(int x)
    {
       int ans=0;
       for(int i=1;i<=num;i++)
        ans+=rank(now[i],x);
       for(int i=1;i<=Num;i++)
        if(x>Now[i])ans++;
        else break;
       return ans;
    }
    int lastans=0;
    inline void work1()
    {
       int x=read()^lastans,y=read()^lastans,k=read()^lastans;
       if(x>y)x^=y^=x^=y;
       num=Num=0;
       pre(root,x,y);
       sort(Now+1,Now+Num+1);
       int l=0,r=inf,ans=0;
       while(l<=r)
       {
         int mid=(l+r)>>1;
         int tmp=Rank(mid)+1;
         if(tmp<=k)
          ans=mid,l=mid+1;
         else
          r=mid-1;
       }
       lastans=ans;
       printf("%d
    ",ans);
    }
    inline void work2()
    {
       int x=read()^lastans,val=read()^lastans;
       Del(root,x,find(x));
       Ins(root,val,x);
    }
    inline void work3()
    {
       int x=read()^lastans,val=read()^lastans;
       Insert(val,x);
    }
    int main()
    {
        Init();
        int n=read();
        for(int i=1;i<=n;i++)
        {
          list[++len]=read();
          lst[len]=New(list[len]);
        }
        root=divide(1,len);
        int Q=read();
        while(Q--)
        {
           char s[2];
           scanf("%s",s);
           if(s[0]=='Q')work1();
           if(s[0]=='M')work2();
           if(s[0]=='I')work3();
        }
        return 0;
    }
    苟利国家生死以, 岂因祸福避趋之。
  • 相关阅读:
    HTML Window.document对象
    HTML JavaScript的DOM操作
    HTML 运算符、类型转换
    HTML JavaScript简介
    js对象学习
    理解MySQL数据库覆盖索引
    mysql 索引2
    mysql 索引
    Extjs 使用图标字体来美化按钮)
    MySql数据类型问题
  • 原文地址:https://www.cnblogs.com/TSHugh/p/7015734.html
Copyright © 2011-2022 走看看