zoukankan      html  css  js  c++  java
  • splay专题复习——bzoj 3224 & 1862 & 1503 题解

    【前言】快要省选二试了。上次去被虐出翔了~~这次即便是打酱油。也要打出风採!

    于是暂停新东西的学习,然后開始复习曾经的知识。为骗分做准备。PS:区间翻转的临时跳过,就算学了也来不及巩固了。

    【BZOJ3224】

    3224: Tyvj 1728 普通平衡树

    Time Limit: 10 Sec  Memory Limit: 128 MB
    Submit: 1477  Solved: 570

    Description

    您须要写一种数据结构(可參考题目标题),来维护一些数,当中须要提供以下操作:
    1. 插入x数
    2. 删除x数(若有多个同样的数,因仅仅删除一个)
    3. 查询x数的排名(若有多个同样的数,因输出最小的排名)
    4. 查询排名为x的数
    5. 求x的前驱(前驱定义为小于x。且最大的数)
    6. 求x的后继(后继定义为大于x。且最小的数)

    Input

    第一行为n,表示操作的个数,以下n行每行有两个数opt和x。opt表示操作的序号(1<=opt<=6)

    Output

    对于操作3,4,5,6每行输出一个数,表示相应答案

    Sample Input

    10
    1 106465
    4 1
    1 317721
    1 460929
    1 644985
    1 84185
    1 89851
    6 81968
    1 492737
    5 493598

    Sample Output

    106465
    84185
    492737


    【分析】写了半个下午。调了一个晚上,这是多么的壮烈啊。

    splay的基本操作。

    由于刚開始复习,这一段的rotate和splay直接copy模板的咕~~(╯﹏╰)b。

    对于同样的数,处理起来非常麻烦。

    YD表示能够用一个count[x]记录x出现的次数。而我是傻的,直接插入进去。这种话,我还记录了num[i][w],表示以i节点的左右子树的大小。(仅仅需在rotate的时候改变一下就可以)

    查询K的排名时,仅仅需把K再插入一遍(<=的形式插),再旋转至根。答案就是num[k][1]+1。

    还有,有关前驱和后继真是烦人(万一有重复!

    )。对此,YY教了我一个乱使的方法。

    每次对于K求出它的前驱(后继)P,假设P<K结束,输出P。否则把P提到根。再做相似的操作。

    【代码】

    /**************************************************************
        Problem: 3224
        User: jiangshibiao
        Language: C++
        Result: Accepted
        Time:696 ms
        Memory:7080 kb
    ****************************************************************/
     
    #include<cstdio>
    #define N 200005
    using namespace std;
    int son[N][3],num[N][3],f[N],a[N];
    int opt,n,i,root,x,node,ans,flag;
    inline void Rotate(int x,int w)//1:左旋。2:右旋 
    {  
        int y=f[x],z=f[y];
        son[y][3-w]=son[x][w];
        if (son[x][w]) f[son[x][w]]=y;
        f[x]=z;
        num[y][3-w]=num[x][w];
        num[x][w]+=num[y][w]+1;
        if (z) 
        {
          if (y==son[z][1]) son[z][1]=x;
          else son[z][2]=x;  
        }
        f[y]=x;son[x][w]=y;
    }  
    inline void splay(int x)  
    {  
        int y;
        while (f[x])  
        {  
            y=f[x]; 
            if (!f[y])  
            {
              if (x==son[y][1]) Rotate(x,2);else Rotate(x,1);
              continue;
            }
            if (y==son[f[y]][1])  
            {
              if (x==son[y][1]) Rotate(y,2),Rotate(x,2);  
              else Rotate(x,1),Rotate(x,2);   
            } 
            else 
            {
              if (x==son[y][2]) Rotate(y,1),Rotate(x,1);   
              else Rotate(x,2),Rotate(x,1); 
            }
        }
        root=x;  
    }
    inline void insert(int x,int add)
    {
      if (add<=a[x]) 
      {
        if (!son[x][1]) son[x][1]=++node,a[node]=add,f[node]=x;
        else insert(son[x][1],add);
        num[x][1]++;
      }
      else
      {
        if (!son[x][2]) son[x][2]=++node,a[node]=add,f[node]=x;
        else insert(son[x][2],add);
        num[x][2]++;
      }
    }
    inline void del(int x,int add)
    {
      if (!x) return;
      if (add==a[x])
      {
        splay(x);
        if (!son[x][1]&&!son[x][2]){root=0;return;}
        if (!son[x][1]) {root=son[x][2];f[son[x][2]]=0;return;}
        if (!son[x][2]) {root=son[x][1];f[son[x][1]]=0;return;}
        int find=son[x][1],temp=son[x][2];
        while (son[find][2]) find=son[find][2];
        splay(find);son[find][2]=temp;f[temp]=find;
        return;
      }
      if (add<a[x]) del(son[x][1],add);else del(son[x][2],add);
    }
    inline int Kth(int x,int add,int now)
    {
      insert(root,add);splay(node);
      int ans=num[node][1]+1;
      del(node,add);
      return ans;
    }
    inline int Ask(int x,int k)
    {
      if (k==num[x][1]+1) return a[x];
      if (k<=num[x][1]) return Ask(son[x][1],k);
      return Ask(son[x][2],k-num[x][1]-1);
    }
    inline int pred(int x,int k)
    {
      int find=son[x][1];if (!find) return 0;
      while (son[find][2]) find=son[find][2];
      if (a[find]!=k) flag=1;return find;
    }
    inline int succ(int x,int k)
    {
      int find=son[x][2];if (!find) return 0;
      while (son[find][1]) find=son[find][1];
      if (a[find]!=k) flag=1;return find;
    }
    int main()
    {
      scanf("%d",&n);root=0;
      for (i=1;i<=n;i++)
      {
        scanf("%d%d",&opt,&x);
        if (opt==1) insert(root,x),splay(node);
        if (opt==2) del(root,x);
        if (opt==3) printf("%d
    ",Kth(root,x,0));
        if (opt==4) printf("%d
    ",Ask(root,x));
        if (opt==5) 
        {
          insert(root,x);splay(node);
          for (flag=0,ans=pred(root,x);!flag||!ans;splay(ans),ans=pred(root,x));
          del(root,x);printf("%d
    ",a[ans]);
        }
        if (opt==6) 
        {
          insert(root,x);splay(node);
          for (flag=0,ans=succ(root,x);!flag||!ans;splay(ans),ans=succ(root,x));
          del(root,x);printf("%d
    ",a[ans]);
        }
      }
      return 0;
    }

    【BZOJ1056/1862】

    1862: [Zjoi2006]GameZ游戏排名系统

    Time Limit: 5 Sec  Memory Limit: 64 MB
    Submit: 554  Solved: 212

    Description

    GameZ为他们最新推出的游戏开通了一个站点。世界各地的玩家都能够将自己的游戏得分上传到站点上。这样就能够看到自己在世界上的排名。得分越高。排名就越靠前。

    当两个玩家的名次同样时,先上传记录者优先。由于新游戏的火爆,站点server已经难堪重负。

    为此GameZ雇用了你来帮他们又一次开发一套新的核心。

    排名系统通常要应付三种请求:上传一条新的得分记录、查询某个玩家的当前排名以及返回某个区段内的排名记录。当某个玩家上传自己最新的得分记录时,他原有的得分记录会被删除。为了减轻server负担。在返回某个区段内的排名记录时。最多返回10条记录。

    Input

    第一行是一个整数n(n>=10)表示请求总数目。

    接下来n行每行包括了一个请求。请求的详细格式例如以下: +Name Score 上传最新得分记录。Name表示玩家名字,由大写英文字母组成,不超过10个字符。Score为最多8位的正整数。 ?Name 查询玩家排名。该玩家的得分记录必然已经在前面上传。

    ?

    Index 返回自第Index名開始的最多10名玩家名字。Index必然合法,即不小于1,也不大于当前有记录的玩家总数。

    输入文件总大小不超过2M。 NOTE:用C++的fstream读大规模数据的效率较低

    Output

    对于每条查询请求。输出相应结果。对于?Name格式的请求。应输出一个整数表示该玩家当前的排名。对于?Index格式的请求,应在一行中依次输出从第Index名開始的最多10名玩家姓名,用一个空格分隔。

    Sample Input

    20
    +ADAM 1000000 增加ADAM的得分记录
    +BOB 1000000 增加BOB的得分记录
    +TOM 2000000 增加TOM的得分记录
    +CATHY 10000000 增加CATHY的得分记录
    ?TOM 输出TOM眼下排名
    ?1 眼下有记录的玩家总数为4,因此应输出第1名到第4名。
    +DAM 100000 增加DAM的得分记录
    +BOB 1200000 更新BOB的得分记录
    +ADAM 900000 更新ADAM的得分记录(即使比原来的差)
    +FRANK 12340000 增加FRANK的得分记录
    +LEO 9000000 增加LEO的得分记录
    +KAINE 9000000 增加KAINE的得分记录
    +GRACE 8000000 增加GRACE的得分记录
    +WALT 9000000 增加WALT的得分记录
    +SANDY 8000000 增加SANDY的得分记录
    +MICK 9000000 增加MICK的得分记录
    +JACK 7320000 增加JACK的得分记录
    ?2 眼下有记录的玩家总数为12,因此应输出第2名到第11名。
    ?

    5 输出第5名到第13名。
    ?KAINE 输出KAINE的排名

    Sample Output

    2
    CATHY TOM ADAM BOB
    CATHY LEO KAINE WALT MICK GRACE SANDY JACK TOM BOB
    WALT MICK GRACE SANDY JACK TOM BOB ADAM DAM

    【分析】我就不吐槽HN抄袭浙江的题目了。。这道题的坑爹之处是字符串的处理。

    怎样推断一个字符串是否出现?哈希本来是个好选择。可是我生怕25万的数据会被卡,仅仅好硬生生地开了一棵字母树。

    为了巩固splay,全是手码的,于是乎调了半天。囧rz。说一下怎样查询。他要找排名从K開始的10个人,我就设L=K,R=K+9。那么我们仅仅需在正常的查询(可參见之前的程序)里把一个固定的值改成一个范围~~

    还有。開始我一直T。后来一气之下,在询问时也找了个点splay一下,然后瞬秒出解。没事多转转~~

    【AC代码】

    /**************************************************************
        Problem: 1862
        User: jiangshibiao
        Language: C++
        Result: Accepted
        Time:1148 ms
        Memory:50828 kb
    ****************************************************************/
     
    #include<cstdio>
    #include<cstring>
    #define N 300005
    using namespace std;
    int son[N][3],Num[N][3],f[N],a[N],b[N],trie[N][27],L[N],score[N],ans[N];
    char Str[N][12],opt[12];
    int k,i,num,add,now,flag,tree,root,node,left,right,tot,Q,l,n,j,PRE;
    void Rotate(int x,int w)
    {
      int y=f[x],z=f[y];
      son[y][3-w]=son[x][w];
      if (son[x][w]) f[son[x][w]]=y;
      f[x]=z;
      Num[y][3-w]=Num[x][w];
      Num[x][w]+=Num[y][w]+1;
      if (z)
        if (son[z][1]==y) son[z][1]=x;else son[z][2]=x;
      f[y]=x;son[x][w]=y;
    }
    inline void splay(int x)  
    {  
        int y;
        while (f[x])  
        {  
            y=f[x]; 
            if (!f[y])  
            {
              if (x==son[y][1]) Rotate(x,2);else Rotate(x,1);continue;
            }
            if (y==son[f[y]][1])  
            {
              if (x==son[y][1]) Rotate(y,2),Rotate(x,2);  
              else Rotate(x,1),Rotate(x,2);   
            } 
            else 
            {
              if (x==son[y][2]) Rotate(y,1),Rotate(x,1);   
              else Rotate(x,2),Rotate(x,1); 
            }
        }
        root=x;  
    }
    inline void insert(int x,int num,int add)
    {
      if (!x)
      {
        root=++node;
        a[node]=add;
        b[node]=num;
        return;
      }
      if (add<a[x]||add==a[x]&&num>b[x]) 
      {
        if (!son[x][1]) son[x][1]=++node,a[node]=add,b[node]=num,f[node]=x;
        else insert(son[x][1],num,add);
        Num[x][1]++;
      }
      else
      {
        if (!son[x][2]) son[x][2]=++node,a[node]=add,b[node]=num,f[node]=x;
        else insert(son[x][2],num,add);
        Num[x][2]++;
      }
    }
    inline void del(int x,int num,int add)
    {
      if (!x) return;
      if (add==a[x]&&num==b[x])
      {
        splay(x);
        int find=son[x][1],temp=son[x][2];
        if (!find&&!temp){root=0;return;}
        if (!find) {root=son[x][2];f[son[x][2]]=0;return;}
        if (!temp) {root=son[x][1];f[son[x][1]]=0;return;}
        while (son[find][2]) find=son[find][2];
        f[son[x][1]]=0;splay(find);Num[find][2]+=Num[temp][1]+Num[temp][2]+1;
        son[find][2]=temp;
        f[temp]=find;root=find;
        return;
      }
      if (add<a[x]||add==a[x]&&num>b[x]) del(son[x][1],num,add);
      else del(son[x][2],num,add);
    }
    int walk(int k,int p)
    {
      if (p==l) {tree=k;return L[k]?L[k]:L[k]=i;}
      if (trie[k][opt[p]-65]) return walk(trie[k][opt[p]-65],p+1);
      trie[k][opt[p]-65]=++tot;flag=0;
      return walk(tot,p+1);
    }
    int Kth(int x,int now)
    {
      if (!x) return 0;
      if (add==a[x]&&num==b[x]) 
      {
        PRE=x;
        return now+1+Num[x][2];
      }
      if (add<a[x]||add==a[x]&&num>b[x]) return Kth(son[x][1],now+Num[x][2]+1);
      return Kth(son[x][2],now);
    }
    void Ask(int x,int now)
    {
      if (!x) return;
      int temp=Num[x][2]+now;
      if (temp>=left) Ask(son[x][2],now);
      if (temp+1>=left&&temp+1<=right) 
      {
        ans[++n]=b[x];
        PRE=x;
      }
      if (temp+1<right) Ask(son[x][1],temp+1);
    }
    void P(int k)
    {
      for (int i=1;i<strlen(Str[k]);i++) putchar(Str[k][i]);
    }
    int main()
    {
      scanf("%d",&Q);tot=1;
      for (i=1;i<=Q;i++)
      {
        scanf("%s",opt);
        l=strlen(opt);
        if (opt[0]=='+')
        {
          scanf("%d",&k);
          flag=1;now=walk(1,1);
          if (flag) 
          {
            del(root,now,score[now]);L[tree]=now=i;
            insert(root,now,k);splay(node);
          }
          else insert(root,now,k),splay(node);
          memcpy(Str[i],opt,sizeof(opt));
          score[now]=k;
        }
        else if (opt[0]=='?'&&opt[1]>='0'&&opt[1]<='9')
        {
          k=0;n=0;
          for (j=1;j<l;j++) k=k*10+opt[j]-48;
          left=k;right=k+9;
          Ask(root,0);
          for (j=1;j<n;j++) P(ans[j]),printf(" ");
          P(ans[n]);printf("
    ");splay(PRE);
        }
        else
        {
          num=walk(1,1);add=score[num];
          printf("%d
    ",Kth(root,0));splay(PRE);
        }
      }
      return 0;
    }

    【BZOJ1503】

    1503: [NOI2004]郁闷的出纳员

    Time Limit: 5 Sec  Memory Limit: 64 MB
    Submit: 5542  Solved: 1968

    Description

    OIER公司是一家大型专业化软件公司,有着数以万计的员工。作为一名出纳员。我的任务之中的一个便是统计每位员工的工资。这本来是一份不错的工作,可是令人郁闷的是,我们的老板重复无常,常常调整员工的工资。假设他心情好,就可能把每位员工的工资加上一个同样的量。反之。假设心情不好,就可能把他们的工资扣除一个同样的量。我真不知道除了调工资他还做什么其他事情。工资的频繁调整非常让员工反感,尤其是集体扣除工资的时候,一旦某位员工发现自己的工资已经低于了合同规定的工资下界,他就会立马气愤地离开公司,而且再也不会回来了。每位员工的工资下界都是统一规定的。每当一个人离开公司。我就要从电脑中把他的工资档案删去,同样,每当公司招聘了一位新员工,我就得为他新建一个工资档案。

    老板常常到我这边来询问工资情况,他并不问详细某位员工的工资情况,而是问如今工资第k多的员工拿多少工资。

    每当这时,我就不得不正确数万个员工进行一次漫长的排序。然后告诉他答案。好了,如今你已经对我的工作了解不少了。正如你猜的那样。我想请你编一个工资统计程序。怎么样,不是非常困难吧?

    Input

    Output

    输出文件的行数为F命令的条数加一。

    对于每条F命令,你的程序要输出一行,仅包括一个整数。为当前工资第k多的员工所拿的工资数,假设k大于眼下员工的数目,则输出-1。

    输出文件的最后一行包括一个整数,为离开公司的员工的总数。

    Sample Input

    9 10
    I 60
    I 70
    S 50
    F 2
    I 30
    S 15
    A 5
    F 1
    F 2

    Sample Output

    10
    20
    -1
    2

    HINT

    I命令的条数不超过100000 A命令和S命令的总条数不超过100 F命令的条数不超过100000 每次工资调整的调整量不超过1000 新员工的工资不超过100000



    【分析】这道题说起来也是辛酸的历史。首先谈谈怎样处理增加和降低工资的问题。我傻叉地开了一个f数组。每次推断一个splay中的结点时,他的实际权值是a[x]+f[q]-f[b[x]-1]。当中a[x]是刚进来的工资,q是如今这个询问,b[x]是x刚进来的时候所在的询问。(有点相似于前缀和的思想)后来网上看了一下。能够直接开一个数组记录~~~

    再说明一下怎样删除。(真是爽。)假设要删除<K的全部结点,我就先插入一个k-1的权值。然后把它旋转到根节点。

    如今,把根节点左边的东东全都咔嚓掉。做完后别忘了删除。

    【代码】

    /**************************************************************
        Problem: 1503
        User: jiangshibiao
        Language: C++
        Result: Accepted
        Time:1372 ms
        Memory:16040 kb
    ****************************************************************/
     
    #include<cstdio>
    #define N 300005
    using namespace std;
    int son[N][3],num[N][3],f[N],a[N],b[N],T[N];
    char Str[N][12],opt[12],s[5];
    int k,i,root,node,P,Min,q,Q,l,n,j,PRE,away;
    void Rotate(int x,int w)
    {
      int y=f[x],z=f[y];
      son[y][3-w]=son[x][w];
      if (son[x][w]) f[son[x][w]]=y;
      f[x]=z;
      num[y][3-w]=num[x][w];
      num[x][w]+=num[y][w]+1;
      if (z)
        if (son[z][1]==y) son[z][1]=x;else son[z][2]=x;
      f[y]=x;son[x][w]=y;
    }
    void splay(int x)
    {
      while (f[x])
      {
        int y=f[x],z=f[y];
        if (!z)
        {
          if (son[y][1]==x) Rotate(x,2);else Rotate(x,1);
          continue;
        }
        if (son[z][1]==y)
        {
          if (son[y][1]==x) Rotate(y,2),Rotate(x,2);
          else Rotate(x,1),Rotate(x,2);
        }
        else
        {
          if (son[y][2]==x) Rotate(y,1),Rotate(x,1);
          else Rotate(x,2),Rotate(x,1);
        }
      }
      root=x;
    }
    void insert(int x)
    {
      if (!x){a[++node]=P;b[node]=q;return;}
      if (P<a[x]+T[q]-T[b[x]-1]) 
      {
        if (son[x][1]) num[x][1]++,insert(son[x][1]);
        else son[x][1]=++node,a[node]=P,b[node]=q,f[node]=x;
      }
      else
      {
        if (son[x][2]) num[x][2]++,insert(son[x][2]);
        else son[x][2]=++node,a[node]=P,b[node]=q,f[node]=x;
      }
    }
    inline void del(int x)
    {
        int find=son[x][1],temp=son[x][2];
        if (!find&&!temp){root=0;return;}
        if (!find) {root=son[x][2];f[son[x][2]]=0;return;}
        if (!temp) {root=son[x][1];f[son[x][1]]=0;return;}
        while (son[find][2]) find=son[find][2];
        f[son[x][1]]=0;splay(find);
        num[find][2]+=num[root][2];
        son[find][2]=temp;f[temp]=find;root=find;
    }
    int Ask(int x,int now)
    {
      int temp=num[x][2]+now;
      if (temp+1==P){PRE=x;return a[x]+T[q]-T[b[x]-1];}
      if (temp>=P) return Ask(son[x][2],now);
      else return Ask(son[x][1],temp+1);
    }
    int main()
    {
      scanf("%d%d",&Q,&Min);
      for (q=1;q<=Q;q++)
      {
        scanf("%s%d",s,&P);T[q]=T[q-1];
        if (s[0]=='I')
        {
          if (P<Min) continue;
          insert(root);splay(node);
        }
        if (s[0]=='A') T[q]+=P;
        if (s[0]=='S') 
        {
          T[q]-=P;P=Min-1;
          insert(root);splay(node);
          son[root][1]=f[son[root][1]]=0;
          away+=num[root][1];num[root][1]=0;
          del(root);
        }
        if (s[0]=='F')
        {
          if (num[root][1]+num[root][2]+1<P||!root) {printf("-1
    ");continue;}
          printf("%d
    ",Ask(root,0));splay(PRE);
        }
      }
      printf("%d",away);
      return 0;
    }
  • 相关阅读:
    Hibernate 学习-3
    Hibernate反向工程使用心得
    MyEclipse中自动整合Spring3+Hibernate/JPA
    jsp页面不显示问题
    jstl获取当前系统时间的方法
    js实现12小时时钟
    从servlet跳到jsp页面,并用jstl 进行判断和显示方法
    jsp调用js文件时出现乱码
    常见异常总结
    js实现表单验证
  • 原文地址:https://www.cnblogs.com/mqxnongmin/p/10717120.html
Copyright © 2011-2022 走看看