2021.12.07 P4291 [HAOI2008]排名系统(Treap)
https://www.luogu.com.cn/problem/P4291
双倍经验:
https://www.luogu.com.cn/problem/P2584
题意:
输入n行,每次输入是一下三种情况之一:
+Name Score 上传最新得分记录。Name表示玩家名字,由大写英文字母组成,不超过10个字符。Score为最多8位的正整数。
?Name 查询玩家排名。该玩家的得分记录必定已经在前面上传。如果两个玩家的得分相同,则先得到该得分的玩家排在前面。
?Index 返回自第Index名开始的最多10名玩家名字。Index必定合法,即不小于1,也不大于当前有记录的玩家总数。
分析:
有分数(名字)找排名、有排名找分数(名字)——平衡树。
不过对于分数的处理有一些特殊:
同样的分数,谁先输入谁在前。蓝书介绍了一种方法:
一般来说,如果有两个需要优化的量 \(v_1\) , \(v_2\) ,要求首先满足 \(v_1\) 最小,在 \(v_1\) 相同的情况下 \(v_2\) 最小,则可以把二者合并成一个量 \(M\times v_1+v_2\) ,其中 \(M\) 是一个比 \(v_2\) 的最大理论值和 \(v_1\) 的最小理论值之差还要大的数。
这题 \(M\) 可以取250000,不过因为题上要求越高排名越靠前,记得再加负号。
代码如下:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<ctime>
#include<map>
#define IOS ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
#define int long long
const int N=250010;
const int M=250000;
int n;
int cnt,root,tot,son[N][2],val[N],key[N],sizei[N],same[N];
int top;
map<string,long long>name_num;
map<long long,string>num_name;
map<int,string>id_name;
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')w=-1;
ch=getchar();
}
while(ch<='9'&&ch>='0'){
s=s*10+ch-'0';
ch=getchar();
}
return s*w;
}
inline void update(int x){
sizei[x]=sizei[son[x][0]]+sizei[son[x][1]]+same[x];
}
inline void rotate(int &x,int flag){
int y=son[x][flag^1];
int change=son[y][flag];
son[x][flag^1]=change;
son[y][flag]=x;
update(x);
update(y);
x=y;
}
inline void insert(int &x,int vali){
if(!x){
x=++cnt;++tot;
sizei[x]=same[x]=1;
val[x]=vali;key[x]=rand();
return ;
}
if(val[x]==vali)return (void)(++sizei[x],++same[x]);
int flag=val[x]<vali;
insert(son[x][flag],vali);
if(key[son[x][flag]]>key[x])rotate(x,flag^1);
update(x);
}
inline void deletei(int &x,int vali){
if(!x)return ;
int flag=val[x]==vali?-1:(val[x]<vali?1:0);
if(flag!=-1)deletei(son[x][flag],vali);
else{
if(!son[x][0]&&!son[x][1]){
--sizei[x];--same[x];
if(!same[x])x=0,--tot;
}else if(son[x][0]&&!son[x][1]){
rotate(x,1);
deletei(son[x][1],vali);
}else if(!son[x][0]&&son[x][1]){
rotate(x,0);
deletei(son[x][0],vali);
}else if(son[x][0]&&son[x][1]){
flag=key[son[x][0]]>key[son[x][1]];
rotate(x,flag);
deletei(son[x][flag],vali);
}
}
update(x);
}
inline int score_rank(int x,int k){
if(!x)return 0;
if(val[x]==k)return sizei[son[x][0]]+1;
else if(val[x]>k)return score_rank(son[x][0],k);
else if(val[x]<k)return sizei[son[x][0]]+same[x]+score_rank(son[x][1],k);
}
inline int rank_score(int x,int k){
if(!x)return 0;
if(sizei[son[x][0]]>=k)return rank_score(son[x][0],k);
else if(sizei[son[x][0]]+same[x]>=k)return val[x];
else return rank_score(son[x][1],k-sizei[son[x][0]]-same[x]);
}
inline int change(string s){
int num=0;
for(int i=0;i<s.length();i++)num=num*10+s[i]-'0';
return num;
}
signed main(){
n=read();
for(int i=1;i<=n;i++){
char op;//=getchar();
cin>>op;
//cout<<"i "<<i<<" "<<op<<endl;
if(op=='+'){
string s;cin>>s;
int x=read();
x=x*M-i;
x*=-1;//!
//cout<<s<<" "<<x<<endl;//
if(name_num[s])deletei(root,name_num[s]);
insert(root,x);
name_num[s]=x;num_name[x]=s;
//cout<<num_name[x]<<" "<<name_num[s]<<endl;//
}else if(op=='?'){
string s;cin>>s;
if(s[0]<'0'||s[0]>'9'){
//cout<<s<<" "<<name_num[s]<<" Case 1"<<endl;
cout<<score_rank(root,name_num[s])<<endl;
}else{
int start=change(s);
//cout<<s<<" "<<start<<" "<<tot<<endl;//
for(int j=start;j<=min(start+9,tot);j++)
cout<<num_name[rank_score(root,j)]<<" ";
cout<<endl;
}
}
/*cout<<"find eleveni "<<endl;
for(int j=1;j<=tot;j++){
int x=rank_score(root,j);
cout<<x<<" "<<num_name[x]<<endl;
}
cout<<endl;*/
}
return 0;
}