题意:有N个数,每次从中任意选取K个数,取其中的最大值,求所有组合能取得的最大值的和.N100,000,K50,输出结果对1000000007取模的结果.0≤每个数10^9​​

样例输入1

5 2
1 2 3 4 5

样例输出1

40

样例输入2

5 3
1 2 3 4 5

样例输出2

45

分析:很显然,这是一道组合数学的题目。其实我们只需要求出每个数作为最大值出现的次数就能够得到答案.如果一个数能够作为最大值出现,那么这个数肯定大于等于第k大的数,先排序.

       我们要找到当前数作为最大数的次数,这由比他小的数来决定.对于样例2,我们考虑3这个数,将3固定在最高位,那么我们还需要确定k-1个数,这k-1个数可以取任意的比3小的数,例如1,2或2,1两个组合,也就是说如果我们当前考虑的数是第i大的数,那么只需要计算出c[i-1][k-1](i-1个数中选k-1个数的方案个数)再乘以这个数即可.

       然而,本题要求取模,不能直接计算组合数,可以使用逆元来计算,但是这样有点复杂,如果我们直接递推则可以直接取模(不涉及到除法),这样有一个问题:内存占用太大,这道题用long long,开数组内存花费太大了,怎么办呢?可以发现递推组合数的时候状态i的结果只与状态i-1的结果有关,所以可以使用滚动数组!代码如下:

 

#include<iostream>
#include<cstdio>
#include<algorithm>

#define maxn 100010

using namespace std;

const int MOD = 1e9 + 7;

long long p[maxn];

long long f[2][maxn];

long long ans;
int k, n;
void work() { f[0][0] = 1; int i, j; bool flag = 1; for (i = 1;i <= n - 1;i++) { int m = min(i,k-1); for (j = 0;j <= m;j++) { if (j == 0 || j == i) f[flag][j] = 1; else f[flag][j] = (f[!flag][j - 1] + f[!flag][j]) % MOD; } if (k - 1 <= i) ans += p[i + 1] * f[flag][k - 1] % MOD; ans %= MOD; flag = !flag; } } int main() { scanf("%d%d", &n, &k); for (int i = 1;i <= n;i++) scanf("%d", &p[i]); sort(p + 1, p + 1 + n); work(); printf("%lld", ans); return 0; }