需要的功能:
①更新一个字符串对应的最新分数
②获得一个字符串对应的最新分数
③获得平衡树中第几名
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);
}
}