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;
}
 posted on 2021-12-07 16:39  eleveni  阅读(42)  评论(0)    收藏  举报