BZOJ1056: [HAOI2008]排名系统 Treap

BZOJ1056/1862: [HAOI2008]排名系统

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 2291  Solved: 658
题解:
Treap按照双关键字排序,第一关键字是权值,第二关键字是时间(也就是第几个被加入的人),这样保证了没有重复的节点(因为第二关键字一定不相同),剩下的就是排名和人名的相互查询的两个操作,用map<string,int>和一个记录数组稍微搞一下就好
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<map>
using namespace std;
const int N=300005;
char c,name[N][20];
int root,sco[N],n;
map<string,int>has;
int siz[N],rs[N],ls[N],rnd[N],a[N],t[N],cnt;
void Pushup(int x)
{
	siz[x]=siz[ls[x]]+siz[rs[x]]+1;
}
void lt(int &x)
{
	int t=rs[x];
	rs[x]=ls[t],ls[t]=x;
	Pushup(x),Pushup(t);
	x=t;
}
void rt(int &x)
{
	int t=ls[x];
	ls[x]=rs[t],rs[t]=x;
	Pushup(x),Pushup(t);
	x=t;
}
void Ins(int &x,int v,int tim)
{
	if(!x)
	{
		x=++cnt,a[x]=v,t[x]=tim,siz[x]=1,rnd[x]=rand();
		return;
	}
	if(v>a[x])
	{
		Ins(ls[x],v,tim);
		if(rnd[ls[x]]>rnd[x]) rt(x);
	}
	else
	{
		Ins(rs[x],v,tim);
		if(rnd[rs[x]]>rnd[x]) lt(x);
	}
	Pushup(x);
}
void Del(int &x,int v,int tim)
{
	if(!x) return;
	if(v==a[x]&&tim==t[x])
	{
		if(ls[x]*rs[x]==0) {x=ls[x]+rs[x];return;}
		if(rnd[ls[x]]>rnd[rs[x]]) rt(x),Del(x,v,tim);
		else lt(x),Del(x,v,tim);
	}
	else if(v>a[x]||v==a[x]&&tim<t[x]) Del(ls[x],v,tim);
	else Del(rs[x],v,tim);
	Pushup(x);
}
int Getrank(int x,int v,int tim)
{
	if(v==a[x]&&tim==t[x]) return siz[ls[x]]+1;
	if(v>a[x]||v==a[x]&&tim<t[x]) return Getrank(ls[x],v,tim);
	else return Getrank(rs[x],v,tim)+siz[ls[x]]+1;
}
int tar;
int Getname(int x,int rank)
{	
	if(rank==siz[ls[x]]+1) return t[x];
	else if(rank<=siz[ls[x]]) return Getname(ls[x],rank);
	else return Getname(rs[x],rank-siz[ls[x]]-1);
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("\n%c",&c);
		scanf(" %s",name[i]);
		int len=strlen(name[i]),tmp,x;
		if(c=='+')
		{
			scanf("%d",&x);
			tmp=has[name[i]];
			if(tmp) Del(root,sco[tmp],tmp);
			Ins(root,x,i);
			has[name[i]]=i;
			sco[i]=x;
		}
		else if(c=='?')
		{
			if(name[i][0]<='9'&&name[i][0]>='0')
			{
				x=0;
				for(int j=0;j<len;j++)
				x=x*10+name[i][j]-'0';
				int mx=min(siz[root],x+9);
				for(int j=x;j<mx;j++)
				printf("%s ",name[Getname(root,j)]);
				printf("%s\n",name[Getname(root,mx)]);
			}
			else
			{
				tmp=has[name[i]];
				printf("%d\n",Getrank(root,sco[tmp],tmp));
			}
		}
	}
}


Description

  排名系统通常要应付三种请求:上传一条新的得分记录、查询某个玩家的当前排名以及返回某个区段内的排名
记录。当某个玩家上传自己最新的得分记录时,他原有的得分记录会被删除。为了减轻服务器负担,在返回某个区
段内的排名记录时,最多返回10条记录。

Input

  第一行是一个整数n(n>=10)表示请求总数目。接下来n行,每行包含了一个请求。请求的具体格式如下: +Na
me Score 上传最新得分记录。Name表示玩家名字,由大写英文字母组成,不超过10个字符。Score为最多8位的正
整数。 ?Name 查询玩家排名。该玩家的得分记录必定已经在前面上传。 ?Index 返回自第Index名开始的最多10名
玩家名字。Index必定合法,即不小于1,也不大于当前有记录的玩家总数。

Output

  对于?Name格式的请求,应输出一个整数表示该玩家当前的排名。对于?Index格式的请求,应在一行中依次输
出从第Index名开始的最多10名玩家姓名,用一个空格分隔。

Sample Input

20
+ADAM 1000000 加入ADAM的得分记录
+BOB 1000000 加入BOB的得分记录
+TOM 2000000 加入TOM的得分记录
+CATHY 10000000 加入CATHY的得分记录
?TOM 输出TOM目前排名
?1 目前有记录的玩家总数为4,因此应输出第1名到第4名。
+DAM 100000 加入DAM的得分记录
+BOB 1200000 更新BOB的得分记录
+ADAM 900000 更新ADAM的得分记录(即使比原来的差)
+FRANK 12340000 加入FRANK的得分记录
+LEO 9000000 加入LEO的得分记录
+KAINE 9000000 加入KAINE的得分记录
+GRACE 8000000 加入GRACE的得分记录
+WALT 9000000 加入WALT的得分记录
+SANDY 8000000 加入SANDY的得分记录
+MICK 9000000 加入MICK的得分记录
+JACK 7320000 加入JACK的得分记录
?2 目前有记录的玩家总数为12,因此应输出第2名到第11名。
?5 输出第5名到第13名。
?KAINE 输出KAINE的排名

Sample Output

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

HINT

N<=250000

Source

[Submit][Status][Discuss]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章