【洛谷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 }