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;
}

 

posted @ 2018-03-08 20:55  泪寒之雪  阅读(295)  评论(0编辑  收藏  举报