BZOJ1056 [HAOI2008]排名系统 treap实现map

需要的功能:

①更新一个字符串对应的最新分数

②获得一个字符串对应的最新分数

③获得平衡树中第几名

hint是哈希,感觉没什么头绪。。

题解存下了字符串和一个哈希内存池,用链式前向星处理hash冲突,用时间戳作为第二比较关键字,在分数相同时检索时间戳

具体来说就是在查询一个字符串时,先在哈希内存池顺着前向星用字符串匹配找到时间戳,

再用分数和时间戳的关键字从treap中找到对应信息(适合题目的较早信息有高rank的设定)

这题核心就在于把时间戳作为第二关键字的运用可以实现简单的map,以及哈希内存池的设置,这些在以前我都没见过。。

#include<bits/stdc++.h>  
//#pragma comment(linker, "/STACK:1024000000,1024000000")   
#include<stdio.h>  
#include<algorithm>  
#include<queue>  
#include<string.h>  
#include<iostream>  
#include<math.h>  
#include<set>  
#include<map>  
#include<vector>  
#include<iomanip>  
using namespace std;  
#define ll long long  
#define pb push_back  
#define FOR(a) for(int i=1;i<=a;i++)  
const int inf=0x3f3f3f3f;  
const int maxn=2e5+5e4+9;    
const int mod=985003;

int n;
int root,sz,tot,head[mod+1];
struct data1{int v,time,nxt;char ch[10];}hash[maxn];
struct data2{int l,r,v,s,rnd,time;char ch[10];}tr[maxn];
void update(int k){tr[k].s=tr[tr[k].l].s+tr[tr[k].r].s+1;}
void rturn(int &k){int t=tr[k].l;tr[k].l=tr[t].r;tr[t].r=k;tr[t].s=tr[k].s;update(k);k=t;}
void lturn(int &k){int t=tr[k].r;tr[k].r=tr[t].l;tr[t].l=k;tr[t].s=tr[k].s;update(k);k=t;}

bool cmp(char a[],char b[]){for(int i=1;i<max(strlen(a),strlen(b));i++)if(a[i]!=b[i])return 0;return 1;}
int Hash(char ch[]){
	int s=0;
	for(int i=1;i<strlen(ch);i++)
	{s*=27;s+=(ch[i]-'A'+1);s%=mod;}
	return s;
}
void del(int &k,int x,int time){
	if(tr[k].v==x){
		if(tr[k].time==time){
			if(tr[k].l*tr[k].r==0)k=tr[k].l+tr[k].r;
			else if(tr[tr[k].l].rnd<tr[tr[k].r].rnd){rturn(k);del(k,x,time);}
			else {rturn(k);del(k,x,time);}
		}
		else if(time>tr[k].time){tr[k].s--;del(tr[k].l,x,time);}
		else {tr[k].s--;del(tr[k].r,x,time);}
	}
	else if(x<tr[k].v){tr[k].s--;del(tr[k].l,x,time);}
	else {tr[k].s--;del(tr[k].r,x,time);}
}
void insert(int &k,char ch[],int x,int time){			
	if(k==0){
		sz++;k=sz;tr[k].v=x;tr[k].s=1;tr[k].rnd=rand();
		memcpy(tr[k].ch,ch,strlen(ch));tr[k].time=time;
		return;
	}
	tr[k].s++;
	if(x<=tr[k].v){insert(tr[k].l,ch,x,time);if(tr[tr[k].l].rnd<tr[k].rnd)rturn(k);}	
	//小于等于都放在左儿子,所以时间大在左边
	else{insert(tr[k].r,ch,x,time);if(tr[tr[k].r].rnd<tr[k].rnd)lturn(k);}
}
void INS(char ch[],int x,int time){		//hash[i]存储hash值为i的尾指针
	int k=Hash(ch);int i=head[k];
	while(i){
		if(cmp(hash[i].ch,ch)){			//在冲突的哈希中检测同名,更新记录
			del(root,hash[i].v,hash[i].time);
			hash[i].time=time;hash[i].v=x;
			insert(root,ch,x,time);
			return;
		}
		i=hash[i].nxt;
	}
	tot++;								//给新的玩家分配一个节点
	hash[tot].time=time;hash[tot].v=x;
	memcpy(hash[tot].ch,ch,strlen(ch));
	hash[tot].nxt=head[k];head[k]=tot;
	insert(root,ch,x,time);
}
int get(char ch[]){						//获取字符串在hash[]数组中的下标
	int k=Hash(ch);int i=head[k];
	while(i){
		if(cmp(hash[i].ch,ch))return i;
		i=hash[i].nxt;
	}
}
int getrank(int k,int x,int time){
	if(k==0)return 0;
	if(tr[k].v==x){				
		if(tr[k].time>time)return getrank(tr[k].r,x,time);	
		else if(tr[k].time<time)return 1+tr[tr[k].r].s+getrank(tr[k].l,x,time);	//先传记录优先
		else return tr[tr[k].r].s+1;
	}
	else if(tr[k].v<x)return getrank(tr[k].r,x,time);
	else return 1+tr[tr[k].r].s+getrank(tr[k].l,x,time);
}
void ask1(char ch[]){	//询问排名
	int t=get(ch);
	printf("%d\n",getrank(root,hash[t].v,hash[t].time));
}
int index(int k,int x){	//获得第x名的树节点编号
	if(tr[tr[k].r].s+1==x)return k;
	else if(x<=tr[tr[k].r].s)return index(tr[k].r,x);
	else return index(tr[k].l,x-tr[tr[k].r].s-1);
}
void ask2(char ch[]){
	int s=0;
	for(int i=1;i<strlen(ch);i++){s*=10;s+=ch[i]-'0';}
	for(int i=s;i<=tot&&i<=s+9;i++){
		printf("%s",tr[index(root,i)].ch+1);
		if(i<tot&&i<s+9)printf(" ");
	}
	printf("\n");
}
int main(){
	scanf("%d",&n);char ch[11];int x;
	for(int i=1;i<=n;i++){
		scanf("%s",ch);
		if(ch[0]=='+'){scanf("%d",&x);INS(ch,x,i);}
		else if(ch[1]>='A'&&ch[1]<='Z')ask1(ch);
		else ask2(ch);
	}
}


posted @ 2017-11-28 21:13  Drenight  阅读(143)  评论(0编辑  收藏  举报