bzoj 1588: [HNOI2002]营业额统计(splay入门)

时间:2022-05-30 15:51:52

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1588

题解:这题如果用普通的bst的话是可以过时间差不多4s左右如果用splay的话是140ms。

由于splay可以有序的插入,各种操作都不会改变中序遍历的结果,而且splay每次可以将插入的位置通过旋转到根的位置,那么这题就可以通过寻找根节点的前驱最大值和后继最小值找到答案而且非常快速。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#define inf 0X3f3f3f3f
using namespace std;
const int M = 1e5 + ;
int pre[M] , key[M] , ch[M][] , root , tot;
int n;
void NewNode(int &r , int fa , int k) {
r = ++tot;
pre[r] = fa;
key[r] = k;
ch[r][] = ch[r][] = ;
}//初始化节点
void Rotate(int x , int kind) {
int y = pre[x];
ch[y][!kind] = ch[x][kind];
pre[ch[x][kind]] = y;
if(pre[y]) ch[pre[y]][ch[pre[y]][] == y] = x;
pre[x] = pre[y];
ch[x][kind] = y;
pre[y] = x;
}//旋转操作一般不改变
void Splay(int r , int goal) {
while(pre[r] != goal) {
if(pre[pre[r]] == goal) Rotate(r , ch[pre[r]][] == r);
else {
int y = pre[r];
int kind = (ch[pre[y]][] == y);
if(ch[y][kind] == r) {
Rotate(r , !kind);
Rotate(r , kind);
}
else {
Rotate(y , kind);
Rotate(r , kind);
}
}
}
if(goal == ) root = r;
}//splay操作一般不改变就是将r点移到goal的子树
void init(int n) {
tot = root = ;
for(int i = ; i <= n ; i++) {
pre[i] = , key[i] = , ch[i][] = ch[i][] = ;
}
}
int Insert(int k) {
int r = root;
while(ch[r][key[r] <= k]) {
if(key[r] == k) {
Splay(r , );
return ;
}//相同的点不插入。
r = ch[r][key[r] <= k];
}
NewNode(ch[r][key[r] <= k] , r , k);
Splay(ch[r][key[r] <= k] , );
return ;
}//插入节点依据点的权值插左右子树
int get_pre(int x) {
int tmp = ch[x][];
if(tmp == ) return inf;
while(ch[tmp][]) tmp = ch[tmp][];
return key[x] - key[tmp];
}//寻找前驱最接近的点。
int get_next(int x) {
int tmp = ch[x][];
if(tmp == ) return inf;
while(ch[tmp][]) tmp = ch[tmp][];
return key[tmp] - key[x];
}//寻找后继最接近的点。
int main() {
int n , x , ans = ;
scanf("%d" , &n);
scanf("%d" , &x);
init(n);
ans += x;
Insert(x);
n--;
while(n--) {
scanf("%d" , &x);
if(Insert(x))
ans += min(get_pre(root) , get_next(root));
}
printf("%d\n" , ans);
return ;
}