题目大意:这道题用到了hash表处理冲突的一种办法,给你一个大小为h的hash表标号从0到h-1,记为hash[0]~hash[h-1]
有两种操作:
1:"+ id ha":表示向哈希表中插入一个 哈希值为h,标号为id的元素,若hash[ha]为空,则直接插入即可,若hash[ha]已被占据,则给一个数m,在(ha+m)%h处插入,若还为非空,则继续在(ha+2*m)%h处插入,直到找到一个位置插入。(题目保证不会出现无法插入的情况)若最后插入的位置为(ha+i*m)%h,则需要耗费i点代价。
2. "- id":表示删除一个标号为id的元素.(保证id在哈希表里)。
现在给出n个操作,问最后所付出的代价为多少。
思路:我们观察可以发现,对于每一个位置,若出现冲突,它的下一个位置是确定的,事实上,它们共同构成一个回路,则我们可以将这个回路变成一条链放到线段树中,线段树实现两种操作,一种是将一个位置填满(赋值为1)或清空(赋值为0),二是返回一个区间中最靠左的空位置的在线段树中的位置,若没有则返回-1.因为一个哈希表中可能存在多条链,我们首先要维护每个位置在那条链上,每条链在线段数中的左右顶点是多少,还有每个位置在线段树中的位置,这可以在预处理中完成,这样一来,对于操作二,直接找到标号id之前插入的位置(我用map实现的),将该位置清空即可,对于操作一,设给你的哈希值为ha,则找到ha在线段树中对应的位置po,和ha所在区间的左右点l,r,先返回[po,r]的最左边的空余位置,若没有,再返回[l,po]的最左边的空余位置。设最后插入的位置为pp,则id所插入的位置为pp(在线段树中)。剩下的就是求代价了,有了pp,po,l,r,这很好求吧,具体实现看代码吧。
#include <iostream> #include <string.h> #include <stdio.h> #include <map> #define maxn 200010 #define mid ((t[p].l+t[p].r)>>1) #define ls (p<<1) #define rs (ls|1) using namespace std; map<int,int> mp; struct tree { int l,r; int po; }t[maxn<<1]; void pushup(int p) { if(t[ls].po!=-1) t[p].po=t[ls].po; else if(t[rs].po!=-1) t[p].po=t[rs].po; else t[p].po=-1; } void build(int p,int l,int r) { t[p].l=l,t[p].r=r; if(l==r) { t[p].po=l; return; } build(ls,l,mid); build(rs,mid+1,r); pushup(p); } void change(int p,int x,int val) { if(t[p].l==t[p].r) { if(val) { t[p].po=-1; } else { t[p].po=t[p].l; } return; } if(x>mid) change(rs,x,val); else change(ls,x,val); pushup(p); } int getpo(int p,int l,int r) { if(t[p].l==l&&t[p].r==r) { return t[p].po; } if(r<=mid) return getpo(ls,l,r); else if(l>mid) return getpo(rs,l,r); else { int ll=getpo(ls,l,mid),rr=getpo(rs,mid+1,r); if(ll!=-1) return ll; if(rr!=-1) return rr; return -1; } } int po[maxn],vis[maxn],lr[maxn][2]; void init(int n,int m) { int i,tmp,num=1,sum=1; lr[0][0]=lr[0][1]=0; for(i=0;i<n;i++) { if(vis[i]) continue; tmp=i; lr[sum][0]=num; while(!vis[tmp]) { po[tmp]=num++; vis[tmp]=sum; tmp=(tmp+m)%n; } lr[sum][1]=num-1; sum++; } } int main() { freopen("dd.txt","r",stdin); int h,m,n,a,b; scanf("%d%d%d",&h,&m,&n); init(h,m); build(1,1,h); char str[2]; long long ans=0; while(n--) { scanf("%s",str); if(str[0]=='+') { scanf("%d%d",&a,&b); int num=vis[b];//b在第num个区间 int l=lr[num][0],r=lr[num][1]; int p=po[b]; int pp=getpo(1,p,r); if(pp!=-1) { ans+=(pp-p); } else { ans+=r-p; pp=getpo(1,l,p); ans+=pp-l+1; } change(1,pp,1); mp.insert(make_pair(a,pp)); } else { scanf("%d",&a); int p=mp.find(a)->second; change(1,p,0); mp.erase(a); } } printf("%I64d\n",ans); return 0; }