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]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章