1. 程式人生 > >最大數的和

最大數的和

using 我們 margin fine 所有 res log 每次 ont

題意:有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; }

最大數的和