正题
题目大意
从
(
n
,
1
)
(n,1)
(n,1)到
(
1
,
1
)
(1,1)
(1,1),一个数组
A
A
A,满足
A
i
≥
A
i
+
1
A_i\geq A_i+1
Ai≥Ai+1
每次有两个选择走到
(
x
−
1
,
y
+
1
)
(x-1,y+1)
(x−1,y+1),或
(
x
,
⌊
y
/
2
⌋
)
(x,\lfloor y/2\rfloor)
(x,⌊y/2⌋)。后者需要消耗
∑
i
=
x
n
A
i
\sum_{i=x}^nA_i
∑i=xnAi的代价
求最小代价
解题思路
先预处理好
B
x
=
∑
i
=
x
n
A
i
B_x=\sum_{i=x}^nA_i
Bx=∑i=xnAi
很容易推出动态转移方程
f
i
,
j
=
m
i
n
{
f
i
−
1
,
j
+
1
,
f
i
,
j
∗
2
+
B
i
}
f_{i,j}=min\{f_{i-1,j+1},f_{i,j*2}+B_i\}
fi,j=min{fi−1,j+1,fi,j∗2+Bi}
然后我们发现首先
A
i
A_i
Ai是有序的,而
B
i
B_i
Bi的性质
设
f
i
,
j
f_{i,j}
fi,j表示放入了前
i
i
i个叶子节点,有
j
j
j个空位的哈夫曼树权值。
然后每次可以加入一个叶子节点在该层,且以后合并代价增加
A
i
A_i
Ai
也可以将两个合并到新一层,空位多一些。
然后就愉快的发现这个的动态转移和之前的一样,其实就是哈夫曼树。
然后每次肯定是优先选择权值最小的合并,就是合并果子原题。
c o d e code code
#include<cstdio>
#define ll long long
using namespace std;
ll a[100010],num,x,n;
long long s,u;
void up(ll x)
{
ll t;
while (x>1 && a[x]<a[x/2])
{
t=a[x];a[x]=a[x/2];a[x/2]=t;
x/=2;
}
}
void down(ll x)
{
ll t,y;
while (x*2<=num && a[x]>a[x*2] || x*2+1<=num && a[x]>a[x*2+1])
{
y=x*2;
if (x*2+1<=num && a[x*2]>a[x*2+1]) y++;
t=a[x];a[x]=a[y];a[y]=t;
x=y;
}
}
void insert(ll x)
{a[++num]=x;up(num);}
int main()
{
int t;
scanf("%lld",&t);
while(t--)
{
s=0;
scanf("%lld",&n);num=0;
for (ll i=1;i<=n;i++)
{
scanf("%lld",&x);
insert(x);
}
while (num>1)
{
u=a[1];a[1]=a[num];num--;down(1);
u+=a[1];a[1]=a[num];num--;down(1);
s+=u;num++;a[num]=u;up(num);
}
printf("%lld\n",s);
}
}