题目描述
有一棵无穷大的满二叉树,根为star,其余所有点的权值为点到根的距离,如图:
现在你有一些扑克牌,点数从1到13,你要把这些扑克牌全部放到这个树上:
1. 当你把点数为i的扑克牌放在权值为j的点上,那么你会得到i*j的分数。
2. 当你把一个扑克牌放在一个节点上,那么你就不能把别的扑克牌放在这个节点以及这个节点的子树上。
你的目标是最小化你的得分。
Input
输入第一行为一个数字N,表示你有的扑克牌数;
接下来一行N个数字,数字在1到13之间。
Output
一个数字,最小得分。
Sample Input
3
5 10 13
Sample Output
43
【数据范围】
30%数据 N<=100
100%数据满足1<=N<=10000.
分析
这题非常像就是合并果子
以往合并果子都是直接用堆排快排的,蒟蒻这次比赛时绝望地码了Splay(才不是不会打小根堆呢)
然后就肥肠简单了
#include <iostream>
#include <cstdio>
#include <algorithm>
#define rep(i,a,b) for (i=a;i<=b;i++)
using namespace std;
int n,ans;
struct node {
int c[2],f;
int cnt,key;
}t[20001];
int rt,cnt;
void Clear(int x) {
t[x].c[0]=t[x].c[1]=t[x].f=t[x].key=t[x].cnt=0;
}
int Get_f(int x) {
return x==t[t[x].f].c[1];
}
void Rotate(int x)
{
int f=t[x].f,gf=t[f].f,lr=Get_f(x);
t[f].c[lr]=t[x].c[lr^1];t[t[f].c[lr]].f=f;
t[x].c[lr^1]=f;t[f].f=x;
t[x].f=gf;
if (gf) t[gf].c[f==t[gf].c[1]]=x;
}
void Splay(int x,int goal) {
for (int f;(f=t[x].f)!=goal;Rotate(x))
if (t[f].f) Rotate((Get_f(x)==Get_f(f))?f:x);
rt=x;
}
void Insert(int key) {
if (!rt) {
cnt++;rt=cnt;
t[cnt].key=key;
t[cnt].cnt++;
t[cnt].c[0]=t[cnt].c[1]=0;
return;
}
int x=rt,f=0;
while (1) {
if (t[x].key==key) {
t[x].cnt++;
Splay(x,0);
return;
}
f=x;x=t[x].c[key>t[x].key];
if (!x) {
cnt++;
t[cnt].key=key;
t[cnt].cnt++;
t[cnt].c[0]=t[cnt].c[1]=0;
t[f].c[key>t[f].key]=cnt;t[cnt].f=f;
Splay(cnt,0);
return;
}
}
}
void Move(int key) {
int x=rt;
while (x) {
if (key<t[x].key)
x=t[x].c[0];
else {
if (t[x].key==key) {
Splay(x,0);
return;
}
x=t[x].c[1];
}
}
}
int Get_small() {
int x=rt;
while (t[x].c[0]) x=t[x].c[0];
return t[x].key;
}
int Get_pre() {
int x=t[rt].c[0];
while (t[x].c[1]) x=t[x].c[1];
return x;
}
void Delete(int key) {
Move(key);
if (t[rt].cnt>1) {
t[rt].cnt--;
return;
}
if (!t[rt].c[1]&&!t[rt].c[0]) {
Clear(rt);rt=0;
return;
}
if (!t[rt].c[1]) {
int oldrt=rt;
rt=t[rt].c[0];
t[rt].f=0;
Clear(oldrt);
return;
}
else
if (!t[rt].c[0]) {
int oldrt=rt;
rt=t[rt].c[1];
t[rt].f=0;
Clear(oldrt);
return;
}
int oldrt=rt;
Splay(Get_pre(),0);
t[rt].c[1]=t[oldrt].c[1];
t[t[rt].c[1]].f=rt;
Clear(oldrt);
}
int main() {
int i,a;
scanf("%d",&n);
rep(i,1,n)
scanf("%d",&a),Insert(a);
while (t[rt].c[0]||t[rt].c[1]) {
int x1=Get_small();
Delete(x1);
int x2=Get_small();
Delete(x2);
ans+=x1+x2;
Insert(x1+x2);
}
printf("%d",ans);
}