BZOJ NOI 十连测 哈夫曼树

【问题描述】
有这样一个经典问题:
∙ 给出一个长度为?的非负整数数组?。
∙ 每次可以选择数组中两个不同位置的数??, ??(? ̸= ?),将它们删除,然
后再向数组中加入一个新的元素,值为?? + ??。
∙ 这样一次操作产生的代价是这个新元素的值,即?? + ??。
∙ 例如当前数组中的数为? = {1, 1, 3, 1},选择?1 = 1, ?4 = 1进行操作
后,数组变为{1, 3, 2},代价为2。
∙ 一共会进行? − 1次操作,要求最小化代价之和。
这道题可以用经典的哈夫曼树算法解决,然而小?正在???考场上,
根本不会哈夫曼树,他决定凭信仰出奇迹。
小?每次选数都是随机选两个数,随机规则为:
∙ 假如有三个数?, ?, ?,可能有相等的数,但是不影响选取。
∙ 小?会等概率选取(?, ?)(?, ?)(?, ?)中的一对。
现在小?想知道他的程序的期望输出。
设期望输出为???,为了避免精度误差,你只需要输出下述式子在
模109 + 7域下的值。
??? ×
? Π︁?=2
?(? − 1)

【数据规模】
对于20%的数据,? ≤ 5。
对于另10%的数据,数组?中最多只有5个正整数。
对于另10%的数据,数组?中的数全部相同。
对于另30%的数据,? ≤ 103。

SOL:我们发现 ans=sigma ai* 一个系数。

我们又发现答案要求的是所有方案的和。

我们不难发现这个系数是关于n的函数。

我们构造一个有n-2个1,一个2的序列。

我们知道n个1通过一步后必然会成上一次的结果。

那么我们可以对n个1的情况构造等式:

 f[n]*n(n个1的答案)=(f[n-1]*n(n-2个1,一个2的答案)+2(合并1次的代价)*S当前方案数)*2Cn(在n个中选2个方案数)

我们不难发现S当前方案数=前n-2个三角型数的积。

所以·O(N)跑一趟就好了。

#include<bits/stdc++.h>
#define mo 1000000007
#define iv2 500000004
using namespace std;
int n,p;
long long f[1000007],s,sum;
signed main () {
    freopen("huffman.in","r",stdin);
    freopen("huffman.out","w",stdout);
    scanf("%d",&n);
    f[1]=0; f[2]=1; s=1;
    for (int i=3;i<=n;i++) {
        f[i]=(f[i-1]*i%mo+2*s)%mo*(i-1)%mo*iv2%mo;
        s=s*i%mo*(i-1)%mo*iv2%mo;
    }
//    cout<<f[n];
    for (int i=1;i<=n;i++) scanf("%d",&p),sum+=p;
    sum%=mo;
    printf("%lld\n",sum*f[n]%mo); return 0;
//    cout<<(7830*7+2*1*3*6*10*15)*6/2;
}

 

转载于:https://www.cnblogs.com/rrsb/p/8530785.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值