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名玩家姓名,用一个空格分隔。
题解:
很明显的平衡树,我用的Splay
题目有3种操作:
- 上传分数:如果这个人名以前出现过,那么就覆盖分数,没出现过就登记分数。 对于是否出现过,我们可以用map来存,然后再处理,出现过就把之前的结点删除,重新插入一个新的结点即可
- 询问某个玩家的当前排名:找到该结点,然后输出其右儿子的size即可,右子树的结点都是比它大的,也就是它的排名了。
- 询问以第k名开始之后的最多10个人的排名记录:直接用整棵树的size减去k就是要查询的区间[k,min(k+10,tot)]的右端点,然后减去10为左端点,将左端点伸展到根节点,右端点伸展到根节点的右儿子,然后后序遍历其子树即可求出各个排名对应的名字
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 3e5+10;
const int MOD = 998244353;
struct node{
int son[2],fa,val,sz; string name;
void Init(int f,int v,string s){ fa=f; sz=1; son[0]=son[1]=0; val=v; name=s; }
void clear(){ fa=sz=val=son[0]=son[1]=0; name=""; }
}t[MAXN];
int root,tot; map<string,int> mp;
inline int id(int x){ return x==t[t[x].fa].son[1]; }
inline void pushup(int x){ t[x].sz=t[t[x].son[0]].sz+t[t[x].son[1]].sz+1; }
inline void rotate(int x){
int y=t[x].fa,z=t[y].fa,k=id(x);
t[z].son[id(y)]=x; t[x].fa=z;
t[y].son[k]=t[x].son[k^1]; t[t[x].son[k^1]].fa=y;
t[x].son[k^1]=y; t[y].fa=x;
pushup(y); pushup(x);
}
inline void splay(int x,int pos){
while(t[x].fa!=pos){
int y=t[x].fa,z=t[y].fa;
if(z!=pos) id(x)==id(y) ? rotate(y):rotate(x);
rotate(x);
}
if(!pos) root=x;
}
inline void Insert(int x,string s){
int u=root,fa=0;
while(u) fa=u,u=t[u].son[x>t[u].val];
u=++tot; t[u].Init(fa,x,s); mp[s]=u;
if(fa) t[fa].son[x>t[fa].val]=u;
splay(u,0);
}
inline int kth(int x){
int u=root; x=max(x,1);
while(1){
if(t[t[u].son[0]].sz>=x) u=t[u].son[0];
else{
x -= t[t[u].son[0]].sz+1;
if(!x) return u;
u = t[u].son[1];
}
}
}
inline int Next(int x,int k){
int u=t[x].son[k];
while(t[u].son[k^1]) u=t[u].son[k^1];
return u;
}
inline void del(int x){
splay(x,0);
int pre=Next(x,0),nxt=Next(x,1);
splay(pre,0); splay(nxt,pre);
int u=t[nxt].son[0];
t[nxt].son[0]=0;
t[u].clear();
pushup(nxt); pushup(pre);
}
inline void update(int val,string name){
del(mp[name]); Insert(val,name);
}
inline void rk(string name){
int u=mp[name];
splay(u,0);
cout<<t[t[u].son[1]].sz<<'\n';
}
void dfs(int u){
if(!u) return;
if(t[u].son[1]) dfs(t[u].son[1]);
cout<<t[u].name<<' ';
if(t[u].son[0]) dfs(t[u].son[0]);
}
inline void query(int k){
k = t[root].sz-k;
int pre=kth(k-10),nxt=kth(k+1);
splay(pre,0); splay(nxt,pre);
int u=t[nxt].son[0];
dfs(u); cout<<'\n';
}
signed main(){
#ifndef ONLINE_JUDGE
freopen("C:\\Users\\Administrator\\Desktop\\in.txt","r",stdin);
#endif // ONLINE_JUDGE
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
Insert(-1," "); Insert(INT_MAX," ");
int Q; cin>>Q;
while(Q--){
string op; cin>>op;
string name=op.substr(1,op.size()-1);
if(op[0]=='+'){
int x; cin>>x;
if(mp[name]) update(x,name);
else Insert(x,name);
}else if(op[0]=='?'){
if(op[1]>='0' && op[1]<='9'){
int k=0;
for(int i=1;i<op.size();i++) k=k*10+op[i]-'0';
query(k);
}else{
string name=op.substr(1,op.size()-1);
rk(name);
}
}
}
return 0;
}
本文介绍了一种使用Splay树优化游戏排名系统的方法,解决了因大量请求导致服务器负担过重的问题。通过平衡树实现上传得分记录、查询玩家排名及返回排名记录等功能,有效提升系统性能。
156

被折叠的 条评论
为什么被折叠?



