『Noi2016十连测第三场 - 哈夫曼树』

pdf版题面 在线提交(需要权限)

题目

问题描述

有这样一个经典问题:

给出一个长度为$n$的非负整数数组$a$。

每次可以选择数组中两个不同位置的数$a_i, a_j(i \neq j)$,将它们删除,然后再向数组中加入一个新的元素,值为$a_i + a_j$。

这样一次操作产生的代价是这个新元素的值,即$a_i + a_j$。

例如当前数组中的数为$a = \{1, 1, 3, 1\}$,选择$a_1 = 1, a_4 = 1$进行操作后,数组变为${1, 3, 2}$,代价为$2$。

一共会进行$n - 1$次操作,要求最小化代价之和。

这道题可以用经典的哈夫曼树算法解决,然而小D正在NOI考场上,根本不会哈夫曼树,他决定凭信仰出奇迹。

D每次选数都是随机选两个数,随机规则为:

  • 假如有三个数$x, y, z$,可能有相等的数,但是不影响选取。
  • D会等概率选取$(x, y)(x, z)(y, z)$中的一对。

现在小D想知道他的程序的期望输出。

设期望输出为$ans$,为了避免精度误差,你只需要输出下述式子在模$10^9 + 7$域下的值。

$$ans \times \prod_{i = 2}^{n} \frac{i(i - 1)}{2}$$

输入格式

从文件huffman.in中读入数据。

第一行一个正整数$n$。

第二行$n$个非负整数$a_i$。

输出格式

输出到文件huffman.out中。

输出一个数表示答案。

样例输入1

5
1 2 2 3 3

样例输出1

5082

样例输入2

点击这里下载

样例输出2

点击这里下载

数据规模

对于$20\%$的数据,$n \leq 5$。

对于另$10\%$的数据,数组$a$中最多只有$5$个正整数。

对于另$10\%$的数据,数组$a$中的数全部相同。

对于另$30\%$的数据,$n \leq 10^3$。

对于$100\%$的数据,$2 \leq n \leq 10^5$,$0 \leq a_i \leq 1000$。

题解

(稍后再补)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll;
#define MOD 1000000007
ll sum;
int n;
ll ans;
ll power(ll x, ll p)
{
if (p == 0)
return 1;
if (p == 1)
return x % MOD;
ll t = power(x, p / 2);
t = (t * t) % MOD;
if (p & 1)
return t * x % MOD;
else
return t;
}
inline ll getrev(ll x)
{
return power(x, MOD - 2);
}
ll s;
int main()
{
freopen("huffman.in", "r", stdin);
freopen("huffman.out", "w", stdout);
scanf("%d", &n);
for (int i = 0; i < n; i++)
{
int a;
scanf("%d", &a);
sum += a;
}
s = 1;
for (int i = 2; i <= n; i++)
s = (s * i) % MOD * (i - 1) % MOD * getrev(2) % MOD;
for (int i = n; i >= 2; i--)
{
ll t = ((s * 2) % MOD * getrev(i) % MOD) % MOD;
ans += sum * t % MOD;
ans %= MOD;
}
printf("%lld\n", ans);
}
文章目录
  1. 1. 题目
    1. 1.1. 问题描述
    2. 1.2. 输入格式
    3. 1.3. 输出格式
    4. 1.4. 样例输入1
    5. 1.5. 样例输出1
    6. 1.6. 样例输入2
    7. 1.7. 样例输出2
    8. 1.8. 数据规模
  2. 2. 题解
  3. 3. 代码
,