Splay 水题,只要写Splay 的插入、查询、查前驱和查后继操作即可。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
const int inf = 0x3f3f3f3f;
int n,m,a[maxn];
vector<int> tmp;
struct Splay_tree {
int ch[maxn][2]; //ch[u][0] 表示 左二子,ch[u][1] 表示右儿子
int f[maxn]; //当前节点的父节点
int sz[maxn]; //当前节点的子树的节点个数
int val[maxn]; //当前节点的权值
int cnt[maxn]; //当前节点所表示的值的个数
int root; //根节点
int tot; //总节点的个数
inline bool get(int x){
return ch[f[x]][1]==x;
}
void init() {
root = tot = 0;
f[0] = ch[0][0] = ch[0][1] = sz[0] = val[0] = cnt[0] = 0;
}
void pushup(int rt) { //维护 rt 的 sz
if(rt) {
sz[rt] = cnt[rt];
if(ch[rt][0]) sz[rt] += sz[ch[rt][0]];
if(ch[rt][1]) sz[rt] += sz[ch[rt][1]];
}
}
void newnode(int rt,int v,int fa) { //新建节点
f[rt] = fa;
val[rt] = v; cnt[rt] = sz[rt] = 1;
ch[rt][0] = ch[rt][1] = 0;
}
void delnode(int rt) {
f[rt] = sz[rt] = val[rt] = 0;
ch[rt][0] = ch[rt][1] = 0;
}
void rotate(int x) { //旋转操作,根据 x 在 f[x] 的哪一侧进行左旋和右旋
int old = f[x], oldf = f[old];
int whichx = get(x);
ch[old][whichx] = ch[x][whichx ^ 1];
f[ch[old][whichx]] = old;
ch[x][whichx ^ 1] = old; f[old] = x;
f[x] = oldf;
if(oldf) ch[oldf][ch[oldf][1] == old] = x;
pushup(old); pushup(x); //不要忘记更新size
}
void splay(int x,int goal) { //将 x 旋到 goal节点 下面
for (int fa = f[x]; fa != goal; rotate(x), fa = f[x]) //再把x翻上来
if (f[fa] != goal) //如果fa非根,且x 和 fa是同一侧,那么先翻转fa,否则先翻转x
rotate((get(x)==get(fa))?fa:x);
if (goal == 0)
root = x;
}
int find(int x) { //查找 x,若 x 不存在返回的是 x 的前驱或者后继 (不一定就是前驱或者后继,两者都可能)
int now = root, father = 0;
if(!now) return now;
while(1) {
if(val[now] == x) return now;
if(ch[now][0] && val[now] > x) {
father = now;
now = ch[now][0];
} else if(ch[now][1] && val[now] < x) {
father = now;
now = ch[now][1];
} else {
return now;
}
}
}
int findx(int x) { //查询第x个数的节点
int now = root;
if (!now) return 0;
while(1) {
if(ch[now][0] && sz[ch[now][0]] >= x) {
now = ch[now][0];
} else {
int sum = cnt[now] + (ch[now][0] ? sz[ch[now][0]] : 0);
if (x <= sum) return now;
x -= sum, now = ch[now][1];
}
}
return now;
}
inline int rank(int x){ //寻找x所在位置(排名)
int now = root,ans=0;
while(1){
if(!now) break;
if(x < val[now])//往左子树搜索
now = ch[now][0];
else {
ans += (ch[now][0] ? sz[ch[now][0]] : 0);
//找到对应的键值,置顶now,返回
if(x==val[now]){splay(now,0); break;}
ans += cnt[now];
now = ch[now][1];//往右子树
}
}
return ans + 1;
}
void insert(int x) { //把 x 插入到 splay中
int now = root, father = 0;
if(root == 0) {
newnode(++tot,x,0);
root = tot;
return ;
}
while(1) {
if(val[now] == x) {
cnt[now]++; sz[now]++;
pushup(now); pushup(f[now]);
splay(now,0);
break;
}
father = now;
now = ch[now][val[now] < x];
if(!now) { //该节点不存在,则新建节点,并把这个节点旋转上去作为根节点
newnode(++tot,x,father);
ch[father][x > val[father]] = tot;
f[tot] = father;
pushup(tot); pushup(father);
splay(tot,0);
break;
}
}
}
int prev(int x) { //查找值 x 的前驱节点
int k = find(x);
splay(k,0);
if(val[k] >= x) {
k = ch[k][0];
while(ch[k][1]) k = ch[k][1];
}
return k;
}
int nxtv(int x) { //查找值 x 的后继节点
int k = find(x);
splay(k,0);
if(val[k] <= x) {
k = ch[k][1];
while(ch[k][0]) k = ch[k][0];
}
return k;
}
void del(int x) { //删除权值为 x 的节点
if(root == 0) return ;
int y = find(x);
splay(y,0);
if(val[root] != x) return ; //不存在
if(cnt[root] > 1) {
cnt[root]--; sz[root]--;
} else if(!ch[root][0] && !ch[root][1]) {
delnode(root); root = 0;
} else if(!ch[root][0]) {
int newroot = ch[root][1];
f[ch[root][1]] = 0;
delnode(root);
root = newroot;
} else if(!ch[root][1]) {
int newroot = ch[root][0];
f[ch[root][0]] = 0;
delnode(root);
root = newroot;
} else {
int pre = prev(x), oldroot = root;
splay(pre,0);
ch[root][1] = ch[oldroot][1];
f[ch[oldroot][1]] = pre;
delnode(oldroot);
pushup(pre);
}
}
int qry(int x) { //处理询问
int ans = inf;
int pre = prev(x), nxt = nxtv(x);
if (pre) ans = min(ans,abs(val[pre] - x));
if (nxt) ans = min(ans,abs(val[nxt] - x));
return ans;
}
}tree;
int main() {
scanf("%d",&n);
tree.init();
long long ans = 0;
for (int i = 1,x; i <= n; i++) {
scanf("%d",&x);
if (i == 1) ans += x;
else {
if(tree.val[tree.find(x)] != x)
ans += tree.qry(x);
}
tree.insert(x);
}
printf("%lld\n",ans);
return 0;
}