哈夫曼树

题目描述
有这样一个经典问题:
给出一个长度为n的非负整数数组a。
每次可以选择数组中两个不同位置的数ai,aj(i ̸= j),将它们删除,然后再向数组中加入一个新的元素,值为ai + aj。
这样一次操作产生的代价是这个新元素的值,即ai + aj。
例如当前数组中的数为a = {1,1,3,1},选择a1 = 1,a4 = 1进行操作后,数组变为{1,3,2},代价为2。
一共会进行n − 1次操作,要求最小化代价之和。
这道题可以用经典的哈夫曼树算法解决,然而小D正在NOI考场上,根本不会哈夫曼树,他决定凭信仰出奇迹。
小D每次选数都是随机选两个数,随机规则为:
假如有三个数x,y,z,可能有相等的数,但是不影响选取。
小D会等概率选取(x,y)(x,z)(y,z)中的一对。
现在小D想知道他的程序的期望输出。
设期望输出为ans,为了避免精度误差,你只需要输出下述式子在模109 + 7域下的值。[ans \times \prod_{i=2}^{n}{\frac{i(i-1)}{2}}]
输入格式
从文件 huffman.in 中读入数据。
第一行一个正整数n。
第二行n个非负整数ai。
输出格式
输出到文件 huffman.out 中。
输出一个数表示答案。
样例
样例输入
5
1 2 2 3 3
样例输出
5082
数据范围与提示
【数据规模】
对于20的数据,n<=5。
对于另10的数据,数组a中最多只有5个正整数。
对于另10的数据,数组a中的数全部相同。
对于另30的数据,n<=103。
对于100的数据,2<=n<=105,0<=ai<=1000。
来源
NOI2016模拟赛 任之洲、洪华墩

可 知 答 案 要 求 的 仅 仅 是 所 有 方 案 的 总 和 。 。 可知答案要求的仅仅是所有方案的总和。。
且 无 论 a [ i ] = ? 在 最 后 答 案 中 系 数 都 一 样 。 且无论a[i]=?在最后答案中系数都一样。 a[i]=?
设 f [ i ] 为 i 个 数 时 的 系 数 设f[i]为i个数时的系数 f[i]i
设 i 个 数 时 总 的 合 并 的 方 案 数 c n t = 3 × 2 × 4 × 3 × . . . × ( i − 2 ) × ( i − 1 ) 设i个数时总的合并的方案数cnt=3\times2\times4\times3\times...\times(i-2)\times(i-1) icnt=3×2×4×3×...×(i2)×(i1)
先 上 柿 子 先上柿子
f [ i ] × n = C n 2 × ( f [ i − 1 ] × n + 2 × c n t ) f[i]\times n=C_{n}^{2} \times(f[i-1]\times n+2\times cnt) f[i]×n=Cn2×(f[i1]×n+2×cnt)

直 观 理 解 就 是 对 于 i 个 数 时 的 答 案 , 视 为 i 个 1 时 , 可 以 枚 举 哪 两 个 数 被 选 。 每 个 选 取 方 式 第 一 步 贡 献 是 2 , 且 总 方 案 数 是 c n t , 所 以 贡 献 时 2 × c n t 。 后 i − 2 步 由 f [ i − 1 ] 推 得 , 贡 献 是 f [ i − 1 ] × ( n − 2 ) + f [ i − 1 ] × 2 直观理解就是对于i个数时的答案,视为i个1时,可以枚举哪两个数被选。每个选取方式第一步贡献是2,且总方案数是cnt,所以贡献时2\times cnt。后i-2步由f[i-1]推得,贡献是f[i-1]\times(n-2)+f[i-1]\times 2 ii12cnt2×cnti2f[i1]f[i1]×(n2)+f[i1]×2
所 以 可 得 : 所以可得:
f [ i ] = ( i − 1 ) × ( f [ i − 1 ] × n + 2 × c n t ) f[i]=(i-1)\times(f[i-1]\times n+2\times cnt) f[i]=(i1)×(f[i1]×n+2×cnt)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define N 100005
using namespace std;
const long long mod=1000000007;
long long s,sum,iv2,f[N];
int n,a[N];
long long power(long long x,long long c){
	long long now=1;
	if(x==0)return 0;
	while(c){
		if(c&1)now=now*x%mod;
		x=x*x%mod;c>>=1;
	}
	return now;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i)scanf("%lld",&a[i]),sum+=a[i];
	iv2=power(2,mod-2);
	//cout<<iv2<<endl;
	f[1]=0;f[2]=1;s=1;
	for(int i=3;i<=n;++i){
		f[i]=(f[i-1]*i%mod+2*s)%mod*(i-1)%mod*iv2%mod;
		s=1LL*i*(i-1)%mod*s%mod*iv2%mod;
	}
	sum%=mod;
	printf("%lld\n",f[n]*sum%mod);
	return 0;
}
/*
5
1 2 2 3 3
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值