zoukankan      html  css  js  c++  java
  • 二逼平衡树

    题目链接:https://www.luogu.org/problemnew/show/P3380

    这题解法真是多,各种数据结构的模板练习题啊。。(在此之前下面四个一个都不会)

    1. 查询k在区间内的排名

    2. 查询区间内排名为k的值

    3. 修改某一位值上的数值

    4. 查询k在区间内的前驱(前驱定义为严格小于x,且最大的数,若不存在输出-2147483647)

    5. 查询k在区间内的后继(后继定义为严格大于x,且最小的数,若不存在输出2147483647)

    1.线段树套平衡树

    对于线段树的每一个节点,开一颗splay,维护序列中的元素

    操作1,类似于线段树区间查询,查询在线段树的点的排名的和 nlognlogn

    操作2,二分答案 nlognlognlogn 

    由于每颗splay的形态不一定一样,所以他不能和线段树一样在树上二分

    看了别人代码学习来的,直接对数的大小进行二分,满足条件的最大数一定在数列上(刚开始还想着在splay上二分 就复杂多了)

    操作3. splay上插入删除 nlognlogn

    操作4(5同理).每颗splay上查找找最大值

    代码个人感觉如果splay和线段树模板打的熟是要比带修主席树好写的

    #include <bits/stdc++.h>
    using namespace std;
    #define mid (h+t)/2
    #define INF 1e9
    #define INF2 2147483647
    const int maxn=51111;
    const int maxn2=2111111;
    int a[maxn],count2[maxn2],leftson[maxn2],rightson[maxn2],
    fa[maxn2],root[maxn*4],data[maxn2],num,n,m;
    bool tt;
    struct re
    {
      int h,t;
    }p[maxn*4];  
    void updata(int x)
    {
      count2[x]=count2[leftson[x]]+count2[rightson[x]]+1;
    }
    void rotate(int x,int y)
    {
        int father=fa[x];
        if (y==1)
        {
            rightson[father]=leftson[x]; 
            if (leftson[x]) fa[leftson[x]]=father;
        } else
        {
            leftson[father]=rightson[x];
            if (rightson[x]) fa[rightson[x]]=father;
        }
        fa[x]=fa[father];
        if (fa[father])
        {
            if (leftson[fa[father]]==father)
              leftson[fa[father]]=x;
            else rightson[fa[father]]=x; 
        }
        fa[father]=x;
        if (y==1) leftson[x]=father; else rightson[x]=father;
        updata(father); updata(x);
    }
    void splay(int x,int goal,int z)
    {
        if (x==root[z]) return;
        int father;
        while (fa[x]!=goal)
        {
            father=fa[x];
            if (fa[father]==goal)
            {
                if (x==leftson[father]) rotate(x,2);
                else rotate(x,1);
            } else
            {
                if (father==leftson[fa[father]])
                {
                    if (x==leftson[father])
                      rotate(father,2),rotate(x,2);
                    else rotate(x,1),rotate(x,2);
                } else
                {
                    if (x==rightson[father])
                      rotate(father,1),rotate(x,1);
                    else rotate(x,2),rotate(x,1);
                }
            }  
        }
        if (goal==0) root[z]=x;
    }
    void insert2(int x,int y,int z)
    {
      while (x)
      {
        count2[x]++;
        if (y<data[x])
        {
          if (!leftson[x]) break;
          x=leftson[x];
        } else
        {
          if (!rightson[x]) break;
          x=rightson[x];
        }
      }
      data[++num]=y; fa[num]=x; count2[num]=1;
      if (x)
      { 
        if (y>=data[x]) rightson[x]=num; 
        else leftson[x]=num;
      }
      splay(num,0,z);
    }
    int query2(int x,int goal)
    {
      int ans=0;
      while (x)
      {
        if (data[x]==goal) tt=1;
        if (data[x]<goal) ans+=count2[leftson[x]]+1,x=rightson[x]; else
        x=leftson[x];
      }
      return(ans);
    }
    int search(int x,int goal)
    {
      while (x)
      {
        if (data[x]==goal) return(x);
        if (data[x]<goal) x=rightson[x];
        else x=leftson[x];
      }
    }
    void delete2(int number,int goal)
    {
      splay(goal,0,number);
      int x=leftson[goal];
      if (x==0)
      {
        root[number]=rightson[goal];
        fa[root[number]]=0; return;
      }
      while (rightson[x]) x=rightson[x];
      splay(x,goal,number);
      rightson[x]=rightson[goal];
      if (rightson[goal]) fa[rightson[goal]]=x;
      updata(x); fa[x]=0; root[number]=x;
    } 
    void build(int x,int h,int t)
    {
       p[x].h=h; p[x].t=t;
       for (int i=h;i<=t;i++) 
       insert2(root[x],a[i],x);
       if (h==t) return;
       build(x*2,h,mid); build(x*2+1,mid+1,t);
    }
    int query(int x,int h,int t,int goal)
    {
      if (p[x].h>t||p[x].t<h) return(0);
      if (h<=p[x].h&&p[x].t<=t)
      {
        return(query2(root[x],goal));
      }
      return(query(x*2,h,t,goal)+query(x*2+1,h,t,goal));
    }
    void delete1(int x,int pos,int goal)
    {
      int h=p[x].h,t=p[x].t;
      int y=search(root[x],goal);
      delete2(x,y); 
      if (h==t) return;
      if (pos<=mid) delete1(x*2,pos,goal); else delete1(x*2+1,pos,goal); 
    }
    void insert1(int x,int pos,int goal)
    {
      int h=p[x].h,t=p[x].t;
      insert2(root[x],goal,x); 
      if (h==t) return;
      if (pos<=mid) insert1(x*2,pos,goal); else insert1(x*2+1,pos,goal);
    }
    int k_th(int x,int y,int k)
    {
      int h=1,t=INF;
      while (h<t)
      {
        int midd=(h+t+1)/2; 
        if (1+query(1,x,y,midd)<=k) h=midd; else t=midd-1;
      }
      return(h);
    }
    int main()
    {
      freopen("noip.in","r",stdin);
      freopen("noip.out","w",stdout);
      cin>>n>>m;
      for (int i=1;i<=n;i++) cin>>a[i];
      build(1,1,n);
      int c,d,e,f;
      for (int i=1;i<=m;i++)
      {
        cin>>c;
        if (c==3) cin>>d>>e; else cin>>d>>e>>f;
        if (c==1)
        {
          cout<<query(1,d,e,f)+1<<endl;
        }
        if (c==2)
        {
          cout<<k_th(d,e,f)<<endl;
        }
        if (c==3)
        {
          delete1(1,d,a[d]);
          insert1(1,d,e);
          a[d]=e;
        }
        if (c==4)
        {
          int x=query(1,d,e,f)+1; 
          if (x==1) cout<<-INF2<<endl; else
          {
            cout<<k_th(d,e,x-1)<<endl;  
          } 
        }
        if (c==5)
        {
          tt=0;
          int x=query(1,d,e,f)+1;
          if (x==e-d+2-tt) cout<<INF2<<endl; else
          {
            cout<<k_th(d,e,x+tt)<<endl;
          }
        }
      }
      return 0;
    }

    似乎得卡常 洛谷不开o2tle

    2.cdq分治(并没有看懂以后再学吧,洛谷上有题解)

    3.树状数组套主席树

    第一维是树状数组,每个里面开一颗线段树(这个刚开始有点难理解,每个节点里维护的其实并不是任何东西的答案,是为了配合树状数组的使用)

    前3个操作就是基本操作吧

    对于4/5,计算出这个数的排名+-1再在树上二分就可以了

    但是允许元素不出现在原序列就导致5的判断很恶心了

    1.查找的时候如果出现在原数列那么是+1不然就是这个排名

    2.还有一个坑 就是检验是不是最后一个数时

    将这个数搞成离散化的数二分的时候 要注意它可能比最后一个数还要大 所以要特殊处理

    *从这个中注意到要注意二分查找的边界限制,显然这个程序中的二分是找到比它小的值,如果这个值比它大那么就要处理了

    *待修主席树的核心还是很好写的 然而这道题的细节就很不友善了

    说一下这一题代码的实现,离散化是数据结构常有的,核心部分很好写,把查询x的排名变为查询比x小的数有多少个再+1(写平衡树写多了很容易忘记这个)

    还有就是空间,(n+m)logn logn 事实我的代码开了100倍,讲道理最坏情况是要500倍左右

    #include <bits/stdc++.h>
    using namespace std;
    #define maxn 100000
    #define INF 2147483647
    int root[maxn],b[maxn],d[maxn],e[maxn],rea[maxn],cnt,num,n,m,lll;
    int tmp1[maxn],tmp2[maxn];
    bool tt;
    struct re
    {
        int a,b,c,d;
    }a[maxn],c[maxn],f[maxn];
    struct ree
    {
        int x,leftson,rightson;
    }p[maxn*50];
    bool cmp(re x,re y)
    {
              if (x.a<y.a) return(true); else return(false);
    }
    void updata(int x)
    {
        p[x].x=p[p[x].leftson].x+p[p[x].rightson].x;
    }
    #define mid (h+t)/2
    void change(int &x,int h,int t,int sum,int l)
    {
        if (!x)
        {
            x=++cnt;
        }
        p[x].x+=l;
        if (h==t) return;
        if (sum<=mid) change(p[x].leftson,h,mid,sum,l);
        else change(p[x].rightson,mid+1,t,sum,l);
        updata(x);
    }
    int query2(int x,int h,int t,int num)
    {
        if (x==0||h>num) return(0);
        if (t<num) return(p[x].x);
        return(query2(p[x].leftson,h,mid,num)+query2(p[x].rightson,mid+1,t,num));
    }
    #define lowbit(x) (x&-x)
    void insert(int x,int y,int l)
    {
        while (x<=n)
        {
            change(root[x],1,lll,y,l);
            x+=lowbit(x);
        }
    }
    int query(int x,int y)
    {
        int ans=0;
        while (x)
        {
            ans+=query2(root[x],1,lll,y);
            x-=lowbit(x);
        }
        return(ans);
    }
    struct ret
    {
        int a,c; bool b;
    };
    int find(int x)
    {
        int h=1,t=num;
        while (h<t)
        {
            if (f[mid].a<x) h=mid+1; else t=mid;
        }
        if (f[h].a==x) tt=1;
        if (f[h].a<x) return(f[h].d+1);
        else return(f[h].d);
    }
    int k_th(int x,int y,int z)
    {
        int num1=0,num2=0; x--;
        while (x)
        {
            tmp1[++num1]=root[x]; x-=lowbit(x);
      } 
      while (y)
      {
          tmp2[++num2]=root[y]; y-=lowbit(y);
        }
        int h=1,t=lll,tmp;
      while (h!=t)
      {
            tmp=0;
            for (int i=1;i<=num1;i++) tmp-=p[p[tmp1[i]].leftson].x;
            for (int i=1;i<=num2;i++) tmp+=p[p[tmp2[i]].leftson].x;
            if (tmp>=z)
            {
            t=mid;
                for (int i=1;i<=num1;i++) tmp1[i]=p[tmp1[i]].leftson;
                for (int i=1;i<=num2;i++) tmp2[i]=p[tmp2[i]].leftson;
              } else
              {
                  h=mid+1;z-=tmp;
                  for (int i=1;i<=num1;i++) tmp1[i]=p[tmp1[i]].rightson;
                  for (int i=1;i<=num2;i++) tmp2[i]=p[tmp2[i]].rightson;
              }
        }
        return (rea[h]);
    }
    bool check(int x,int y,int goal)
    {
        int num1=0,num2=0; x--;
        while (x)
        {
            tmp1[++num1]=root[x]; x-=lowbit(x);
      } 
      while (y)
      {
          tmp2[++num2]=root[y]; y-=lowbit(y);
        }
        int h=1,t=lll,tmp=0;
        while (h!=t)
        {
            if (goal<=mid)
            {
                 t=mid;
               for (int i=1;i<=num1;i++) tmp1[i]=p[tmp1[i]].leftson;
             for (int i=1;i<=num2;i++) tmp2[i]=p[tmp2[i]].leftson;
            } else
            {
                 h=mid+1;
               for (int i=1;i<=num1;i++) tmp1[i]=p[tmp1[i]].rightson;
               for (int i=1;i<=num2;i++) tmp2[i]=p[tmp2[i]].rightson;
            }
        }
         for (int i=1;i<=num1;i++) tmp-=p[tmp1[i]].x;
       for (int i=1;i<=num2;i++) tmp+=p[tmp2[i]].x;
       if (tmp) return(1); else return(0);
    }
    int main()
    {
          std::ios::sync_with_stdio(false); 
          freopen("noip.in","r",stdin);
          freopen("noip.out","w",stdout);
          cin>>n>>m;
          for (int i=1;i<=n;i++) cin>>a[i].a,f[i].a=a[i].a,f[i].b=i,f[i].c=0;
          num=n;
          for (int i=1;i<=m;i++)
          {
                cin>>b[i];
                if (b[i]==1) cin>>c[i].a>>d[i]>>e[i];
                if (b[i]==2) cin>>c[i].a>>d[i]>>e[i];
                if (b[i]==3)
                {
                    cin>>c[i].a>>d[i];
                    f[++num].a=d[i]; f[num].b=i;f[num].c=1;
                    }
                    if (b[i]==4) cin>>c[i].a>>d[i]>>e[i];
                    if (b[i]==5) cin>>c[i].a>>d[i]>>e[i];
            }
            sort(f+1,f+num+1,cmp); f[0].a=-INF;
            for (int i=1;i<=num;i++)
            {
                if (f[i].a!=f[i-1].a) lll++; f[i].d=lll; rea[lll]=f[i].a;
                if (f[i].c==0) a[f[i].b].b=lll,a[f[i].b].c=i;
                else c[f[i].b].b=lll,c[f[i].b].c=i;
            }
            //for (int i=1;i<=num;i++) cout<<f[i].a<<endl;
            for (int i=1;i<=n;i++)
            {
                insert(i,a[i].b,1);
            }
            for (int i=1;i<=m;i++)
            {
                if (b[i]==1)
                {
                    int x=find(e[i]);
                    cout<<1+query(d[i],x)-query(c[i].a-1,x)<<endl;  //zhuyi0
                }
                if (b[i]==2)
                {
                    cout<<k_th(c[i].a,d[i],e[i])<<endl; 
                }
                if (b[i]==3)
                {
              insert(c[i].a,a[c[i].a].b,-1);
              insert(c[i].a,c[i].b,1);
              a[c[i].a].b=c[i].b;
              } 
              if (b[i]==4)
              {
                  int x=find(e[i]);
                  int y=query(d[i],x)-query(c[i].a-1,x)+1;
                  if (y==1) cout<<-INF<<endl;
              else cout<<k_th(c[i].a,d[i],y-1)<<endl;
                }
                if (b[i]==5)
                {
                    tt=0;int x=find(e[i]);
                    if(tt==1&&check(c[i].a,d[i],x)) tt=1; else tt=0;
                  int y=query(d[i],x)-query(c[i].a-1,x)+1;
                  if (y==d[i]-c[i].a+2-tt) cout<<INF<<endl;
              else cout<<k_th(c[i].a,d[i],y+tt)<<endl;
                }
            }
    }

    4.分块

  • 相关阅读:
    VS2008 环境中完美搭建 Qt 4.7.4 静态编译的调试与发布 Inchroy's Blog 博客频道 CSDN.NET
    编写可丢弃的代码
    c++ using namespace std; 海明威 博客园
    解决MySQL server has gone away
    nginx upstream 调度策略
    (2006, 'MySQL server has gone away') 错误解决 dba007的空间 51CTO技术博客
    Linux IO模型漫谈(2) 轩脉刃 博客园
    redis源码笔记 initServer 刘浩de技术博客 博客园
    MySQLdb批量插入数据
    词库的扩充百度百科的抓取你知道这些热词吗? rabbit9898 ITeye技术网站
  • 原文地址:https://www.cnblogs.com/yinwuxiao/p/8419947.html
Copyright © 2011-2022 走看看