splay专题复习——bzoj 3224 & 1862 & 1503 题解

来源:互联网 发布:2016年桂林旅游数据 编辑:程序博客网 时间:2024/05/16 08:35

【前言】快要省选二试了。上次去被虐出翔了~~这次即便是打酱油,也要打出风采!于是暂停新东西的学习,然后开始复习以前的知识,为骗分做准备。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 200005using 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\n",Kth(root,x,0));    if (opt==4) printf("%d\n",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\n",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\n",a[ans]);    }  }  return 0;}

【BZOJ1056/1862】

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

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

Description

GameZ为他们最新推出的游戏开通了一个网站。世界各地的玩家都可以将自己的游戏得分上传到网站上。这样就可以看到自己在世界上的排名。得分越高,排名就越靠前。当两个玩家的名次相同时,先上传记录者优先。由于新游戏的火爆,网站服务器已经难堪重负。为此GameZ雇用了你来帮他们重新开发一套新的核心。排名系统通常要应付三种请求:上传一条新的得分记录、查询某个玩家的当前排名以及返回某个区段内的排名记录。当某个玩家上传自己最新的得分记录时,他原有的得分记录会被删除。为了减轻服务器负担,在返回某个区段内的排名记录时,最多返回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 300005using 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("\n");splay(PRE);    }    else    {      num=walk(1,1);add=score[num];      printf("%d\n",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 300005using 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\n");continue;}      printf("%d\n",Ask(root,0));splay(PRE);    }  }  printf("%d",away);  return 0;}

0 2
原创粉丝点击