jozj4010-我才不是萝莉控呢【哈夫曼树】

421 篇文章 4 订阅

正题


题目大意

( 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 AiAi+1
每次有两个选择走到 ( x − 1 , y + 1 ) (x-1,y+1) (x1,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{fi1,j+1,fi,j2+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);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值