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

本文介绍了一种使用Splay树优化游戏排名系统的方法,解决了因大量请求导致服务器负担过重的问题。通过平衡树实现上传得分记录、查询玩家排名及返回排名记录等功能,有效提升系统性能。

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种操作:

  1. 上传分数:如果这个人名以前出现过,那么就覆盖分数,没出现过就登记分数。 对于是否出现过,我们可以用map来存,然后再处理,出现过就把之前的结点删除,重新插入一个新的结点即可
  2. 询问某个玩家的当前排名:找到该结点,然后输出其右儿子的size即可,右子树的结点都是比它大的,也就是它的排名了。
  3. 询问以第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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值