题目链接
解题思路
这道题目要求的是所有集合的最大值和最小值的乘积的和,所以只需要关注最小值和最大值。
我们现在假设集合中有4个数:2、5、8、10
ps:我们先不考虑像
(
2
,
2
)
(2,2)
(2,2) 这样的特殊情况。
首先看第一个数2,以2为最大值构成不了集合。
接着看第二个数5,以5为最大值构成的集合有:
(
2
,
5
)
(2,5)
(2,5):最小值是2。
接着看第三个数8,以8为最大值构成的集合有:
(
2
,
8
)
,
(
2
,
5
,
8
)
(2,8),(2,5,8)
(2,8),(2,5,8):最小值是2。
(
5
,
8
)
(5,8)
(5,8) :最小值是5。
最后看第四个数10,以10为最大值构成的集合有:
(
2
,
10
)
,
(
2
,
5
,
10
)
,
(
2
,
8
,
10
)
,
(
2
,
5
,
8
,
10
)
(2,10),(2,5,10),(2,8,10),(2,5,8,10)
(2,10),(2,5,10),(2,8,10),(2,5,8,10):最小值是2。
(
5
,
10
)
,
(
5
,
8
,
10
)
(5,10),(5,8,10)
(5,10),(5,8,10):最小值是5。
(
8
,
10
)
(8,10)
(8,10):最小值是8。
上面的枚举是以最大值为标准展开的,除了像
(
2
,
2
)
(2,2)
(2,2) 这样的特殊情况,其他的集合都枚举出来了。我们来看第二次和第三次枚举之间的关系,第二次枚举最小值只有2,且2只出现1次;第三次枚举最小值2出现了2次;第四次枚举最小值2出现了4次,并且最小值5出现了2次,是第三次枚举的2倍。
由此可以看出相邻两次枚举最小值的和之间的关系了:第i次枚举出现的最小值的和 = 第i-1次枚举出现的最小值的和的2倍 + 第i-1大的值。
上面的结论是建立在最先给出的数是按升序排列的,当然,我们可以对题目给的数排序。
我们来看2倍是怎么出现的:以第三次和第四次枚举为例,第三次枚举2和8之间有一个数5,当以2为最小值,8为最大值时,只有两种选择,选5和不选5;第四次枚举2和10之间有两个数5和8,这两个数各有两个选择,选或不选,这样就是4种选择。
接下来就可以写代码了:用 a
存储输入,dp[i]
表示以 a[i]
为(a
已排过序)最大值所能确定的集合的最小值之和。有如下递推式 dp[i]=dp[i-1]*2 + a[i-1]
,最终结果是
∑
0
n
−
1
(
d
p
[
i
]
∗
a
[
i
]
+
a
[
i
]
∗
a
[
i
]
)
\sum_0^{n-1}(dp[i] * a[i] + a[i]*a[i])
∑0n−1(dp[i]∗a[i]+a[i]∗a[i]),
a
[
i
]
∗
a
[
i
]
a[i]*a[i]
a[i]∗a[i] 表示集合只有一个的特殊情况。
ps:dp[0]
的值为0,因为不存在以a[0]
为最大值的集合
源代码如下:
#include <cstdio>
#include <iostream>
#include <algorithm>
#define NUM 100500
#define MOD 1000000007
#define ll long long
using namespace std;
ll dp[NUM];
ll a[NUM];
ll solve(int n) {
for (int i = 1; i < n; ++i) {
dp[i] = (2*dp[i-1] + a[i-1]) % MOD;
}
ll res = 0;
for (int i = 0; i < n; ++i) {
res = (res + dp[i]*a[i] % MOD + a[i]*a[i] % MOD) % MOD;
}
return res;
}
int main(int argc, char** argv) {
int n;
cin >> n;
for (int i = 0; i < n; ++i) {
scanf("%lld", a+i);
}
sort(a, a+n);
ll res = solve(n);
printf("%lld\n", res);
return 0;
}