首页 > 代码库 > splay专题复习——bzoj 3224 & 1862 & 1503 题解
splay专题复习——bzoj 3224 & 1862 & 1503 题解
【前言】快要省选二试了。上次去被虐出翔了~~这次即便是打酱油。也要打出风採!于是暂停新东西的学习。然后開始复习曾经的知识,为骗分做准备。PS:区间翻转的临时跳过,就算学了也来不及巩固了。
【BZOJ3224】
3224: Tyvj 1728 普通平衡树
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1477 Solved: 570
Description
您须要写一种数据结构(可參考题目标题)。来维护一些数,当中须要提供下面操作:
1. 插入x数
2. 删除x数(若有多个同样的数,因仅仅删除一个)
3. 查询x数的排名(若有多个同样的数。因输出最小的排名)
4. 查询排名为x的数
5. 求x的前驱(前驱定义为小于x,且最大的数)
6. 求x的后继(后继定义为大于x。且最小的数)
Input
第一行为n,表示操作的个数,以下n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)
Output
对于操作3,4,5,6每行输出一个数,表示相应答案
Sample Input
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598
Sample Output
84185
492737
【分析】写了半个下午。调了一个晚上,这是多么的壮烈啊。
splay的基本操作。由于刚開始复习,这一段的rotate和splay直接copy模板的咕~~(╯﹏╰)b。
对于同样的数。处理起来非常麻烦。YD表示能够用一个count[x]记录x出现的次数。
而我是傻的,直接插入进去。这种话,我还记录了num[i][w],表示以i节点的左右子树的大小。(仅仅需在rotate的时候改变一下就可以)
查询K的排名时,仅仅需把K再插入一遍(<=的形式插),再旋转至根,答案就是num[k][1]+1。
还有,有关前驱和后继真是烦人(万一有反复。)。对此,YY教了我一个乱使的方法。
每次对于K求出它的前驱(后继)P。假设P<K结束,输出P。否则把P提到根,再做类似的操作。
【代码】
/************************************************************** Problem: 3224 User: jiangshibiao Language: C++ Result: Accepted Time:696 ms Memory:7080 kb ****************************************************************/ #include<cstdio> #define N 200005 using namespace std; int son[N][3],num[N][3],f[N],a[N]; int opt,n,i,root,x,node,ans,flag; inline void Rotate(int x,int w)//1:左旋;2:右旋 { int y=f[x],z=f[y]; son[y][3-w]=son[x][w]; if (son[x][w]) f[son[x][w]]=y; f[x]=z; num[y][3-w]=num[x][w]; num[x][w]+=num[y][w]+1; if (z) { if (y==son[z][1]) son[z][1]=x; else son[z][2]=x; } f[y]=x;son[x][w]=y; } inline void splay(int x) { int y; while (f[x]) { y=f[x]; if (!f[y]) { if (x==son[y][1]) Rotate(x,2);else Rotate(x,1); continue; } if (y==son[f[y]][1]) { if (x==son[y][1]) Rotate(y,2),Rotate(x,2); else Rotate(x,1),Rotate(x,2); } else { if (x==son[y][2]) Rotate(y,1),Rotate(x,1); else Rotate(x,2),Rotate(x,1); } } root=x; } inline void insert(int x,int add) { if (add<=a[x]) { if (!son[x][1]) son[x][1]=++node,a[node]=add,f[node]=x; else insert(son[x][1],add); num[x][1]++; } else { if (!son[x][2]) son[x][2]=++node,a[node]=add,f[node]=x; else insert(son[x][2],add); num[x][2]++; } } inline void del(int x,int add) { if (!x) return; if (add==a[x]) { splay(x); if (!son[x][1]&&!son[x][2]){root=0;return;} if (!son[x][1]) {root=son[x][2];f[son[x][2]]=0;return;} if (!son[x][2]) {root=son[x][1];f[son[x][1]]=0;return;} int find=son[x][1],temp=son[x][2]; while (son[find][2]) find=son[find][2]; splay(find);son[find][2]=temp;f[temp]=find; return; } if (add<a[x]) del(son[x][1],add);else del(son[x][2],add); } inline int Kth(int x,int add,int now) { insert(root,add);splay(node); int ans=num[node][1]+1; del(node,add); return ans; } inline int Ask(int x,int k) { if (k==num[x][1]+1) return a[x]; if (k<=num[x][1]) return Ask(son[x][1],k); return Ask(son[x][2],k-num[x][1]-1); } inline int pred(int x,int k) { int find=son[x][1];if (!find) return 0; while (son[find][2]) find=son[find][2]; if (a[find]!=k) flag=1;return find; } inline int succ(int x,int k) { int find=son[x][2];if (!find) return 0; while (son[find][1]) find=son[find][1]; if (a[find]!=k) flag=1;return find; } int main() { scanf("%d",&n);root=0; for (i=1;i<=n;i++) { scanf("%d%d",&opt,&x); if (opt==1) insert(root,x),splay(node); if (opt==2) del(root,x); if (opt==3) printf("%d\n",Kth(root,x,0)); if (opt==4) printf("%d\n",Ask(root,x)); if (opt==5) { insert(root,x);splay(node); for (flag=0,ans=pred(root,x);!flag||!ans;splay(ans),ans=pred(root,x)); del(root,x);printf("%d\n",a[ans]); } if (opt==6) { insert(root,x);splay(node); for (flag=0,ans=succ(root,x);!flag||!ans;splay(ans),ans=succ(root,x)); del(root,x);printf("%d\n",a[ans]); } } return 0; }
【BZOJ1056/1862】
1862: [Zjoi2006]GameZ游戏排名系统
Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 554 Solved: 212
Description
GameZ为他们最新推出的游戏开通了一个站点。世界各地的玩家都能够将自己的游戏得分上传到站点上。这样就能够看到自己在世界上的排名。得分越高。排名就越靠前。
当两个玩家的名次同样时。先上传记录者优先。因为新游戏的火爆。站点server已经难堪重负。
为此GameZ雇用了你来帮他们又一次开发一套新的核心。排名系统通常要应付三种请求:上传一条新的得分记录、查询某个玩家的当前排名以及返回某个区段内的排名记录。
当某个玩家上传自己最新的得分记录时,他原有的得分记录会被删除。
为了减轻server负担。在返回某个区段内的排名记录时,最多返回10条记录。
Input
第一行是一个整数n(n>=10)表示请求总数目。接下来n行每行包括了一个请求。请求的详细格式例如以下: +Name Score 上传最新得分记录。Name表示玩家名字,由大写英文字母组成。不超过10个字符。Score为最多8位的正整数。
?Name 查询玩家排名。该玩家的得分记录必然已经在前面上传。
?Index 返回自第Index名開始的最多10名玩家名字。
Index必然合法。即不小于1,也不大于当前有记录的玩家总数。
输入文件总大小不超过2M。
NOTE:用C++的fstream读大规模数据的效率较低
Output
对于每条查询请求。输出对应结果。
对于?Name格式的请求。应输出一个整数表示该玩家当前的排名。对于?
Index格式的请求,应在一行中依次输出从第Index名開始的最多10名玩家姓名。用一个空格分隔。
Sample Input
+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
CATHY TOM ADAM BOB
CATHY LEO KAINE WALT MICK GRACE SANDY JACK TOM BOB
WALT MICK GRACE SANDY JACK TOM BOB ADAM DAM
【分析】我就不吐槽HN抄袭浙江的题目了。。这道题的坑爹之处是字符串的处理。怎样推断一个字符串是否出现?哈希本来是个好选择,可是我生怕25万的数据会被卡,仅仅好硬生生地开了一棵字母树。。
为了巩固splay,全是手码的。于是乎调了半天,囧rz。
说一下怎样查询。他要找排名从K開始的10个人,我就设L=K,R=K+9。那么我们仅仅需在正常的查询(可參见之前的程序)里把一个固定的值改成一个范围~~
还有,開始我一直T,后来一气之下。在询问时也找了个点splay一下。然后瞬秒出解。没事多转转~~
【AC代码】
/************************************************************** Problem: 1862 User: jiangshibiao Language: C++ Result: Accepted Time:1148 ms Memory:50828 kb ****************************************************************/ #include<cstdio> #include<cstring> #define N 300005 using namespace std; int son[N][3],Num[N][3],f[N],a[N],b[N],trie[N][27],L[N],score[N],ans[N]; char Str[N][12],opt[12]; int k,i,num,add,now,flag,tree,root,node,left,right,tot,Q,l,n,j,PRE; void Rotate(int x,int w) { int y=f[x],z=f[y]; son[y][3-w]=son[x][w]; if (son[x][w]) f[son[x][w]]=y; f[x]=z; Num[y][3-w]=Num[x][w]; Num[x][w]+=Num[y][w]+1; if (z) if (son[z][1]==y) son[z][1]=x;else son[z][2]=x; f[y]=x;son[x][w]=y; } inline void splay(int x) { int y; while (f[x]) { y=f[x]; if (!f[y]) { if (x==son[y][1]) Rotate(x,2);else Rotate(x,1);continue; } if (y==son[f[y]][1]) { if (x==son[y][1]) Rotate(y,2),Rotate(x,2); else Rotate(x,1),Rotate(x,2); } else { if (x==son[y][2]) Rotate(y,1),Rotate(x,1); else Rotate(x,2),Rotate(x,1); } } root=x; } inline void insert(int x,int num,int add) { if (!x) { root=++node; a[node]=add; b[node]=num; return; } if (add<a[x]||add==a[x]&&num>b[x]) { if (!son[x][1]) son[x][1]=++node,a[node]=add,b[node]=num,f[node]=x; else insert(son[x][1],num,add); Num[x][1]++; } else { if (!son[x][2]) son[x][2]=++node,a[node]=add,b[node]=num,f[node]=x; else insert(son[x][2],num,add); Num[x][2]++; } } inline void del(int x,int num,int add) { if (!x) return; if (add==a[x]&&num==b[x]) { splay(x); int find=son[x][1],temp=son[x][2]; if (!find&&!temp){root=0;return;} if (!find) {root=son[x][2];f[son[x][2]]=0;return;} if (!temp) {root=son[x][1];f[son[x][1]]=0;return;} while (son[find][2]) find=son[find][2]; f[son[x][1]]=0;splay(find);Num[find][2]+=Num[temp][1]+Num[temp][2]+1; son[find][2]=temp; f[temp]=find;root=find; return; } if (add<a[x]||add==a[x]&&num>b[x]) del(son[x][1],num,add); else del(son[x][2],num,add); } int walk(int k,int p) { if (p==l) {tree=k;return L[k]?L[k]:L[k]=i;} if (trie[k][opt[p]-65]) return walk(trie[k][opt[p]-65],p+1); trie[k][opt[p]-65]=++tot;flag=0; return walk(tot,p+1); } int Kth(int x,int now) { if (!x) return 0; if (add==a[x]&&num==b[x]) { PRE=x; return now+1+Num[x][2]; } if (add<a[x]||add==a[x]&&num>b[x]) return Kth(son[x][1],now+Num[x][2]+1); return Kth(son[x][2],now); } void Ask(int x,int now) { if (!x) return; int temp=Num[x][2]+now; if (temp>=left) Ask(son[x][2],now); if (temp+1>=left&&temp+1<=right) { ans[++n]=b[x]; PRE=x; } if (temp+1<right) Ask(son[x][1],temp+1); } void P(int k) { for (int i=1;i<strlen(Str[k]);i++) putchar(Str[k][i]); } int main() { scanf("%d",&Q);tot=1; for (i=1;i<=Q;i++) { scanf("%s",opt); l=strlen(opt); if (opt[0]==‘+‘) { scanf("%d",&k); flag=1;now=walk(1,1); if (flag) { del(root,now,score[now]);L[tree]=now=i; insert(root,now,k);splay(node); } else insert(root,now,k),splay(node); memcpy(Str[i],opt,sizeof(opt)); score[now]=k; } else if (opt[0]==‘?‘&&opt[1]>=‘0‘&&opt[1]<=‘9‘) { k=0;n=0; for (j=1;j<l;j++) k=k*10+opt[j]-48; left=k;right=k+9; Ask(root,0); for (j=1;j<n;j++) P(ans[j]),printf(" "); P(ans[n]);printf("\n");splay(PRE); } else { num=walk(1,1);add=score[num]; printf("%d\n",Kth(root,0));splay(PRE); } } return 0; }
【BZOJ1503】
1503: [NOI2004]郁闷的出纳员
Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 5542 Solved: 1968
Description
OIER公司是一家大型专业化软件公司。有着数以万计的员工。
作为一名出纳员。我的任务之中的一个便是统计每位员工的工资。这本来是一份不错的工作,可是令人郁闷的是,我们的老板重复无常,常常调整员工的工资。假设他心情好,就可能把每位员工的工资加上一个同样的量。
反之,假设心情不好。就可能把他们的工资扣除一个同样的量。我真不知道除了调工资他还做什么其他事情。
工资的频繁调整非常让员工反感,尤其是集体扣除工资的时候,一旦某位员工发现自己的工资已经低于了合同规定的工资下界,他就会立马气愤地离开公司。而且再也不会回来了。
每位员工的工资下界都是统一规定的。每当一个人离开公司,我就要从电脑中把他的工资档案删去,同样,每当公司招聘了一位新员工。我就得为他新建一个工资档案。
老板常常到我这边来询问工资情况,他并不问详细某位员工的工资情况。而是问如今工资第k多的员工拿多少工资。每当这时,我就不得不正确数万个员工进行一次漫长的排序。然后告诉他答案。好了。如今你已经对我的工作了解不少了。正如你猜的那样,我想请你编一个工资统计程序。怎么样,不是非常困难吧?
Input
Output
输出文件的行数为F命令的条数加一。对于每条F命令,你的程序要输出一行,仅包括一个整数。为当前工资第k多的员工所拿的工资数,假设k大于眼下员工的数目,则输出-1。输出文件的最后一行包括一个整数,为离开公司的员工的总数。
Sample Input
I 60
I 70
S 50
F 2
I 30
S 15
A 5
F 1
F 2
Sample Output
20
-1
2
HINT
I命令的条数不超过100000 A命令和S命令的总条数不超过100 F命令的条数不超过100000 每次工资调整的调整量不超过1000 新员工的工资不超过100000
【分析】这道题说起来也是辛酸的历史。
首先谈谈怎样处理添加和降低工资的问题。
我傻叉地开了一个f数组。每次推断一个splay中的结点时。他的实际权值是a[x]+f[q]-f[b[x]-1]。当中a[x]是刚进来的工资。q是如今这个询问,b[x]是x刚进来的时候所在的询问。(有点类似于前缀和的思想)后来网上看了一下,能够直接开一个数组记录~~~
再说明一下怎样删除。(真是爽!
)如果要删除<K的全部结点。我就先插入一个k-1的权值。然后把它旋转到根节点。
如今。把根节点左边的东东全都咔嚓掉。做完后别忘了删除。
【代码】
/************************************************************** Problem: 1503 User: jiangshibiao Language: C++ Result: Accepted Time:1372 ms Memory:16040 kb ****************************************************************/ #include<cstdio> #define N 300005 using namespace std; int son[N][3],num[N][3],f[N],a[N],b[N],T[N]; char Str[N][12],opt[12],s[5]; int k,i,root,node,P,Min,q,Q,l,n,j,PRE,away; void Rotate(int x,int w) { int y=f[x],z=f[y]; son[y][3-w]=son[x][w]; if (son[x][w]) f[son[x][w]]=y; f[x]=z; num[y][3-w]=num[x][w]; num[x][w]+=num[y][w]+1; if (z) if (son[z][1]==y) son[z][1]=x;else son[z][2]=x; f[y]=x;son[x][w]=y; } void splay(int x) { while (f[x]) { int y=f[x],z=f[y]; if (!z) { if (son[y][1]==x) Rotate(x,2);else Rotate(x,1); continue; } if (son[z][1]==y) { if (son[y][1]==x) Rotate(y,2),Rotate(x,2); else Rotate(x,1),Rotate(x,2); } else { if (son[y][2]==x) Rotate(y,1),Rotate(x,1); else Rotate(x,2),Rotate(x,1); } } root=x; } void insert(int x) { if (!x){a[++node]=P;b[node]=q;return;} if (P<a[x]+T[q]-T[b[x]-1]) { if (son[x][1]) num[x][1]++,insert(son[x][1]); else son[x][1]=++node,a[node]=P,b[node]=q,f[node]=x; } else { if (son[x][2]) num[x][2]++,insert(son[x][2]); else son[x][2]=++node,a[node]=P,b[node]=q,f[node]=x; } } inline void del(int x) { int find=son[x][1],temp=son[x][2]; if (!find&&!temp){root=0;return;} if (!find) {root=son[x][2];f[son[x][2]]=0;return;} if (!temp) {root=son[x][1];f[son[x][1]]=0;return;} while (son[find][2]) find=son[find][2]; f[son[x][1]]=0;splay(find); num[find][2]+=num[root][2]; son[find][2]=temp;f[temp]=find;root=find; } int Ask(int x,int now) { int temp=num[x][2]+now; if (temp+1==P){PRE=x;return a[x]+T[q]-T[b[x]-1];} if (temp>=P) return Ask(son[x][2],now); else return Ask(son[x][1],temp+1); } int main() { scanf("%d%d",&Q,&Min); for (q=1;q<=Q;q++) { scanf("%s%d",s,&P);T[q]=T[q-1]; if (s[0]==‘I‘) { if (P<Min) continue; insert(root);splay(node); } if (s[0]==‘A‘) T[q]+=P; if (s[0]==‘S‘) { T[q]-=P;P=Min-1; insert(root);splay(node); son[root][1]=f[son[root][1]]=0; away+=num[root][1];num[root][1]=0; del(root); } if (s[0]==‘F‘) { if (num[root][1]+num[root][2]+1<P||!root) {printf("-1\n");continue;} printf("%d\n",Ask(root,0));splay(PRE); } } printf("%d",away); return 0; }
splay专题复习——bzoj 3224 & 1862 & 1503 题解