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的排名
+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
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