关于平衡树的练习,以后关于平衡树的题都放这儿了
1、HNOI2002营业额统计:古董题了,题目大意就是维护一个数列,只有一个操作,在数列中插入一个数,计算出数列中与这个数之差的最小绝对值,统计总和。
裸的平衡树模板题,方便一点可以用set+lower_boud+upper_bound秒杀,我写了一下splay,速度还凑活
2、NOI2004郁闷的出纳员:题目大意是维护一个数列,并且支持5种操作,1、插入一个元素,2、删除值小于标准的元素,3、数列中的每一个数的值都增加A,4、数列中的每一个数的值都减小A.,5、输出工资第K大的人的工资数目
这道题也可以用平衡树来维护,由于它的增加减少操作的特殊性(数列中的每个元素都要进行增加或者减少),所以我们不能傻乎乎的一个个搞,可以在外部记录一个变量,表示当前工资的改变量,当一个数要插入时,把这个数减去工资改变量之后插入,当你要输出该元素工资时,可以吧数列中要输出的元素加上工资的改变量,如果你要删除的时候,你可以去删除小于最低工资标准减去工资改变量的元素(这样类似与前缀和优化)。剩下的操作就类似与平衡树的细节了。
#include <cstdio>
#include <cstdlib>
using namespace std;
const int maxn = 100000;
int money,n,v,change,numm,leave=0;
char c;bool first = true;
struct node{
int key,left,right,lson,rson,father;
};
class splayTree{
public:
void zig(int x){
int y = tree[x].father;
tree[y].left = tree[x].right;
if (tree[x].right!=0)
tree[tree[x].right].father = y;
tree[x].father = tree[y].father;
if (tree[y].father!=0){
if (y == tree[tree[y].father].left)
tree[tree[y].father].left = x;
else
tree[tree[y].father].right = x;
}
tree[x].right = y;
tree[y].father = x;
tree[y].lson = tree[x].rson;
tree[x].rson = tree[y].lson + tree[y].rson + 1;
}
void zag(int x){
int y = tree[x].father;
tree[y].right = tree[x].left;
if (tree[x].left!=0)
tree[tree[x].left].father = y;
tree[x].father = tree[y].father;
if (tree[y].father!=0){
if (y == tree[tree[y].father].left)
tree[tree[y].father].left = x;
else
tree[tree[y].father].right = x;
}
tree[x].left = y;
tree[y].father = x;
tree[y].rson = tree[x].lson;
tree[x].lson = tree[y].lson + tree[y].rson + 1;
}
void splay(int now){
while (tree[now].father!=0){
int t = tree[now].father;
if (tree[t].father == 0){
if (now == tree[t].left) zig(now); else zag(now);
break;
}
if (now == tree[t].left){
if (t == tree[tree[t].father].left)
zig(t),zig(now);
else
zig(now),zag(now);
}else{
if (t == tree[tree[t].father].right)
zag(t),zag(now);
else
zag(now),zig(now);
}
}
root = now;
}
void insert(int x){
if (numm==0){
tree[++num].key = x;
numm=1;root = num;
return;
}
int t = root;
tree[++num].key = x;
numm++;
while (true){
if (x<=tree[t].key){
if (tree[t].left==0){
tree[t].left = num;tree[t].lson ++ ;
tree[num].father = t;
break;
}else t = tree[t].left;
}else{
if (tree[t].right == 0){
tree[t].right = num;tree[t].rson ++;
tree[num].father = t;
break;
}else t = tree[t].right;
}
}
while (tree[t].father!=0){
if (t == tree[tree[t].father].left)
tree[tree[t].father].lson++;
else
tree[tree[t].father].rson++;
t = tree[t].father;
}
splay(num);
}
void del(int x){
insert(x);
numm=numm-tree[root].lson-1;
leave+=tree[root].lson;
root = tree[root].right;
tree[root].father = 0;
}
void find(int x){
int t = root;
if (x<=0 || x>tree[t].lson+tree[t].rson+1) {printf("-1\n");return;}
while (true){
if (x>=tree[t].lson+1){
if (x == tree[t].lson+1){
printf("%d\n",tree[t].key+change);
break;
}else {x=x-(tree[t].lson+1);t = tree[t].right;}
}else t = tree[t].left;
}
}
node tree[maxn];
int num,root;
}spt;
int main(){
freopen("cashier.in","r",stdin);
freopen("cashier.out","w",stdout);
scanf("%d %d\n",&n,&money);
for (int i=1;i<=n;++i){
scanf("%c %d\n",&c,&v);
switch (c){
case 'I':if (v>=money) spt.insert(v-change);break;
case 'A':change+=v;break;
case 'S':change-=v;spt.del(money-change);break;
case 'F':spt.find(numm-v+1);break;
}
}printf("%d\n",leave);
return 0;
}
3、HNOI2004宠物收养所:题目大意就是维护一个数列,数列里面保存着宠物的特点值,或者人想要的宠物的特点值,求出与新插入的元素之差最小的绝对值之和。
题目有一个性质,就是如果你是人去领养宠物,如果收养所里有宠物,那么你可以立即收养,如果没有宠物,那么你人就会在收养所里预订一个名额,下次如果有宠物进来了,那么你可以优先选择,这就保证了宠物收养所里面要不都是宠物,要不都是人的预订名额。所以我们可以给这个数列设定一个标记,表示收养所里的是宠物还是人的预订。根据不同的新插入元素,对于不同的标记进行不同的操作,这题用不着写个冗长的平衡树,用set+lower_bound搞搞差不多了,400+字节可以AC,速度当然比手写的平衡树要慢一点。。。
4、ZOJI2007报表统计:题目大意是有一个数列,数列每个元素都是一个链表,并把这些链表按照数列的顺序顺次链接起来形成一个大的链表。该问题要支持3种操作,1、在某一个元素代表的链表的末尾插入一个元素,2、询问数列中相邻2个数的差值的绝对值的最小值,3、询问数列中任意2个数的差值的绝对值的最小值。
第二个操作我们可以通过一个multiset来完成,每插入一个元素,显而易见,会形成2个新的差值,把这两个差值加入multiset,另一个方面,当你加入一个元素的时候,也会使原来存在的一个差值不存在(详见题目),我们只需要知道这个差值是多少,然后在multiset中用find函数找到它,并用erase删除就可以了。
第三个操作我们可以知道任意两个数的差值最小必然存在于这两个数在排序后的数列中是相邻的,因此我们用splay来维护这些数,每次插入一个数,都在splay中求前趋和后继,然后更新一下目前最小值,一个优化就是,如果你当前的最小值已经为0了,那么下一次插入的时候就不用去找前趋后继之类来更新最小值了。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <set>
#include <algorithm>
using namespace std;
multiset<int>::iterator it;
const int maxn = 500000+10,INF = 0x7FFFFFFF;
int A[maxn],B[maxn],l,r;
int MIN_GAP = INF,MIN_SORT_GAP = INF;
int succ,prev,num = 1,root,n,m;
struct node{
int father,left,right,lson,rson,key;
};
class splayTree{
public:
void zig(int x){
int y = tree[x].father;
tre