【洛谷P2584】【ZJOI2006】GameZ游戏排名系统题解

题目链接

题意:

GameZ为他们最新推出的游戏开通了一个网站。世界各地的玩家都可以将自己的游戏得分上传到网站上。这样就可以看到自己在世界上的排名。得分越高,排名就越靠前。当两个玩家的名次相同时,先上传记录者优先。由于新游戏的火爆,网站服务器已经难堪重负。为此GameZ雇用了你来帮他们重新开发一套新的核心。

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

输入格式:

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

接下来n行每行包含了一个请求。请求的具体格式如下:

+Name Score 上传最新得分记录。Name表示玩家名字,由大写英文字母组成,不超过10个字符。Score为最多10位的正整数。

?Name 查询玩家排名。该玩家的得分记录必定已经在前面上传。

?Index 返回自第Index名开始的最多10名玩家名字。Index必定合法,即不小于1,也不大于当前有记录的玩家总数。输入文件总大小不超过2M。

 

 

NOTE:用C++的fstream读大规模数据的效率较低

输出格式:

对于每条查询请求,输出相应结果。

对于?Name格式的请求,应输出一个整数表示该玩家当前的排名。

对于?Index格式的请求,应在一行中依次输出从第Index名开始的最多10名玩家姓名,用一个空格分隔。

样例输入:

20
+ADAM 1000000
+BOB 1000000 
+TOM 2000000
+CATHY 10000000
?TOM 
?1
+DAM 100000 
+BOB 1200000
+ADAM 900000 
+FRANK 12340000 
+LEO 9000000
+KAINE 9000000 
+GRACE 8000000 
+WALT 9000000 
+SANDY 8000000 
+MICK 9000000 
+JACK 7320000 
?2 
?5  
?KAINE

样例输出:

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

时空限制:

每个测试点1s,128MB

数据范围:

n≥10

输入文本大小≤2MB

得分Score是最多十位的正整数

名字字符串最多十位

题解:

明显是一道Splay的题,不过还是需要一些技巧。

2MB文本太大怎么读入?题目已经提示用fstream很慢,所以我们在读入整数时用快读,读入字符串时一个一个getchar而不是用C++的string。

怎么知道字符串对应的节点?用map!

怎么知道节点对应的字符串?我们可以把字符串的Hash值存入节点中,注意这里Hash不用取模,因为字符串最多10位,而且取模后从Hash值还原成字符串是一件十分困难的事。

Splay一定要用双旋!一定要用双旋!一定要用双旋!不然会被极端数据(能够把Splay退化成链的)卡掉。

除此之外,还需要一些卡常技巧(如把i++改成++i,自己定义min和max代替algorithm中的,在函数前加inline)。

参考代码如下:

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4 #include<map>
  5 #define max(a,b) ((a)>(b)?a:b)
  6 #define min(a,b) ((a)<(b)?a:b)
  7 #define ll long long
  8 #define maxid 250005
  9 using namespace std;
 10 struct Splay{
 11     int ch[2],fa,times,size;
 12     ll score,hash;
 13     Splay(){
 14         ch[0]=ch[1]=fa=times=size=score=hash=0;
 15     }
 16     Splay(int f,ll has,ll scr,int tms){//新建节点的构造函数
 17         ch[0]=ch[1]=0;
 18         fa=f;
 19         times=tms;
 20         size=1;
 21         score=scr;
 22         hash=has;
 23     }
 24 }node[maxid];
 25 map<ll,int>tono;//Hash映射到Splay节点的编号
 26 int splayid=0,super;//super是Splay大根的下标
 27 inline void maintain(int p){
 28     node[p].size=node[node[p].ch[0]].size+node[node[p].ch[1]].size+1;
 29 }
 30 inline void rot(int&p,int lr){//0:左旋 1:右旋
 31     int faa=node[p].fa;
 32     int k=node[p].ch[lr^1];
 33     node[p].ch[lr^1]=node[k].ch[lr];
 34     node[node[p].ch[lr^1]].fa=p;
 35     maintain(p);
 36     node[p].fa=k;
 37     node[k].ch[lr]=p;
 38     maintain(k);
 39     node[k].fa=faa;
 40     p=k;
 41 }
 42 inline void ins(int&p,int fa,ll hash,ll score,int times){//插入节点
 43     if(!p){//新建节点
 44         node[p=++splayid]=Splay(fa,hash,score,times);
 45         return;
 46     }
 47     if(node[p].score!=score?node[p].score<score:node[p].times>times)ins(node[p].ch[0],p,hash,score,times);
 48     else ins(node[p].ch[1],p,hash,score,times);
 49     maintain(p);
 50 }
 51 inline int qrank(int p){//查询节点p的排名
 52     int ans=node[node[p].ch[0]].size+1;
 53     int fa=node[p].fa;
 54     while(fa){
 55         if(p==node[fa].ch[1])ans+=node[node[fa].ch[0]].size+1;
 56         p=fa;
 57         fa=node[p].fa;
 58     }
 59     return ans;
 60 }
 61 inline int qid(int p,int rank){//查询排名为rank的节点
 62     if(rank<=node[node[p].ch[0]].size)return qid(node[p].ch[0],rank);
 63     else if(rank==node[node[p].ch[0]].size+1)return p;
 64     else return qid(node[p].ch[1],rank-node[node[p].ch[0]].size-1);
 65 }
 66 inline void splay(int p){//将p旋到根
 67     int fa=node[p].fa;
 68     while(fa){//还没到根节点
 69         int ga=node[fa].fa;//爷爷
 70         if(ga&&((node[ga].ch[0]==fa&&node[fa].ch[0]==p)||(node[ga].ch[1]==fa&&node[fa].ch[1]==p))){//一字型,双旋
 71             int&pt=node[ga].fa?(node[node[ga].fa].ch[0]==ga?node[node[ga].fa].ch[0]:node[node[ga].fa].ch[1]):super;
 72             if(node[ga].ch[0]==fa){
 73                 rot(pt,1);
 74                 rot(pt,1);
 75             }else{
 76                 rot(pt,0);
 77                 rot(pt,0);
 78             }
 79             p=pt;
 80             fa=node[p].fa;
 81         }else{//普通单旋
 82             int&pt=ga?(node[ga].ch[0]==fa?node[ga].ch[0]:node[ga].ch[1]):super;
 83             if(p==node[fa].ch[0])rot(pt,1);
 84             else rot(pt,0);
 85             p=pt;
 86             fa=node[p].fa;
 87         }
 88     }
 89 }
 90 bool toright;//是否到达了右哨兵
 91 inline void pt(ll hash){//打印字符串
 92     if(!hash){
 93         toright=1;
 94         return;
 95     }
 96     char s[15];
 97     s[14]=0;
 98     int pos=14;
 99     while(hash){
100         s[--pos]=hash%27-1+'A';
101         hash/=27;
102     }
103     printf("%s ",s+pos);
104 }
105 inline void print(int&p,int rank){//打印字符串
106     if(rank<=node[node[p].ch[0]].size)print(node[p].ch[0],rank);
107     else if(rank>1+node[node[p].ch[0]].size)print(node[p].ch[1],rank-node[node[p].ch[0]].size-1);
108     else pt(node[p].hash);
109 }
110 inline ll rd(){//快读
111     ll ans=0;
112     char c=0;
113     while(c<'0'||c>'9')c=getchar();
114     while(c>='0'&&c<='9'){
115         ans=ans*10LL+c-'0';
116         c=getchar();
117     }
118     return ans;
119 }
120 int main(){
121     int n=rd();
122     ins(super,0,0,1000000000000000000LL,0);//左(前)哨兵
123     ins(super,0,0,-1000000000000000000LL,0);//右(后)哨兵
124     for(int times=1;times<=n;++times){//时间顺序
125         char tp=0;
126         while(tp!='+'&&tp!='?')tp=getchar();
127         if(tp=='+'){
128             ll hash=0;
129             char ch=getchar();
130             while(ch>='A'&&ch<='Z'){
131                 hash=hash*27+ch-'A'+1;//将字符串Hash,不用取模,因为最多只有10位字母
132                 ch=getchar();
133             }
134             ll score=rd();
135             if(tono[hash]){//存在,需要先删除原来的节点
136                 int rnk=qrank(tono[hash]);
137                 splay(qid(super,rnk-1));
138                 splay(qid(super,rnk+1));
139                 if(node[node[super].ch[0]].ch[1]){
140                     node[node[super].ch[0]].ch[1]=0;
141                     maintain(node[super].ch[0]);
142                     maintain(super);
143                 }else{
144                     int pre=node[node[super].ch[0]].ch[0];
145                     node[super].ch[0]=pre;
146                     node[pre].fa=super;
147                     maintain(super);
148                 }
149             }
150             ins(super,0,hash,score,times);
151             tono[hash]=splayid;
152             splay(splayid);
153         }else{
154             char ch=getchar();
155             if(ch>='0'&&ch<='9'){//查10个人名
156                 int num=0;
157                 while(ch>='0'&&ch<='9'){
158                     num=num*10+ch-'0';
159                     ch=getchar();
160                 }
161                 toright=0;
162                 for(int i=1;i<=10;++i){
163                     print(super,num+i);
164                     if(toright)break;
165                     splay(qid(super,num+i));
166                 }
167                 putchar('\n');
168             }else{//查排名
169                 ll hash=0;
170                 while(ch>='A'&&ch<='Z'){
171                     hash=hash*27+ch-'A'+1;
172                     ch=getchar();
173                 }
174                 int ans=qrank(tono[hash]);
175                 splay(tono[hash]);
176                 printf("%d\n",ans-1);//有左哨兵,所以要-1
177             }
178         }
179     }
180     return 0;
181 }
posted on 2018-10-26 15:37  AndyGamma  阅读(294)  评论(0编辑  收藏  举报