正题
题目大意
从(n,1)(n,1)(n,1)到(1,1)(1,1)(1,1),一个数组AAA,满足Ai≥Ai+1A_i\geq A_i+1Ai≥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=xnAi\sum_{i=x}^nA_i∑i=xnAi的代价
求最小代价
解题思路
先预处理好Bx=∑i=xnAiB_x=\sum_{i=x}^nA_iBx=∑i=xnAi
很容易推出动态转移方程
fi,j=min{fi−1,j+1,fi,j∗2+Bi}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}
然后我们发现首先AiA_iAi是有序的,而BiB_iBi的性质
设fi,jf_{i,j}fi,j表示放入了前iii个叶子节点,有jjj个空位的哈夫曼树权值。
然后每次可以加入一个叶子节点在该层,且以后合并代价增加AiA_iAi
也可以将两个合并到新一层,空位多一些。
然后就愉快的发现这个的动态转移和之前的一样,其实就是哈夫曼树。
然后每次肯定是优先选择权值最小的合并,就是合并果子原题。
codecodecode
#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);}
}