弹钢琴

来源:互联网 发布:软件编程技术培训 编辑:程序博客网 时间:2024/04/30 23:49

题目描述

Flsy的妈妈出去买菜了,他让Flsy在家里好好练习弹钢琴。小C的钢琴很奇怪,一共有N个琴键,每一个琴键有一个价值Ai。当Flsy弹钢琴时,他会同时按住K个不同的琴键,但是只有价值最大的琴键会发出声音。Flsy想练习所有不同的按键组合,那么他能弹奏出声音的琴键的价值之和(同一个琴键在不同的组合中被弹出声音需要重复计算),最终的答案对1e9+7取模。

输入

输入第一行,两个整数N和K。 输入第二行,N个整数Ai。

输出

输出仅一行,为题目中的答案。

题目分析:
关键字:组合数(数学),乘法逆元,DP,终态枚举
题目的想法很直接,就是先按琴键的值排序,终态枚举每个琴键,使之的值为最大值,从前面的数中去k-1个排列组合加在ans上即可。
其实就是求排列组合的裸题,数据相对也不算很大,可以之间递推得到,其实差不多算是DP的一个前身,还有就是乘法逆元,下面将一一讲解。

首先是递推:
类似于DP,定义数组C[i][j]表示从i中取出j个数的方案数
发现组合数可以转移:C[i][j]=C[i-1][j-1]+C[i-1][j];
就可以每次把上一层的组合数都转移过来即可,代码如下:

#include<cstdio>#include<algorithm>using namespace std;#define N_MAX 100005#define MOD 1000000007int n,k,ans;int dp[N_MAX][55],A[N_MAX];int main(){    scanf("%d %d",&n,&k);    for(int i=1; i<=n; i++) scanf("%d",&A[i]);    sort(A+1,A+1+n);    dp[0][0]=1;    for(int i=1; i<=n; i++) {        for(int j=1; j<=k; j++) dp[i][j]=(dp[i-1][j-1]+dp[i-1][j])%MOD;        ans=(ans+1LL*dp[i][k]*A[i])%MOD;    }    printf("%d\n",ans);    return 0;}

接下来是乘法逆元(乘法逆元是怎么来的呢?这里不解释,直接运用,之后可能写一个关于乘法逆元的专题)

组合数与乘法逆元相组合可以得到以下的公式:
C(i,j)=i! * ((i-j)!)^(MOD-2) * (j!)^(MOD-2) (这里对MOD也是有要求的,应该是质数,10^9+7显然是质数,所以MOD可以这么用)

发现本题还要更特殊一点,因为发现求组合数时k-1是不变的,所以就可以简化上述公式为C[i][j]=C[i-1][j]a (a-b)^(MOD-2)(还不会私我……)

代码如下:

#include<bits/stdc++.h>using namespace std;#define K_MAX 55#define N_MAX 100005#define MOD 1000000007#define LL long long/*    求组合数C[i][k-1] | n>=i>=k-1    乘法逆元:F[i][j]=F[i-1][j]*a*(a-b)^(MOD-2); */int A[N_MAX];LL dp[N_MAX][55];int n,k,ans;void add(int &a,LL b) {    b%=MOD;    a+=b;    if(a>=MOD) a-=MOD;}int fst(int x,int n) {//快速幂    int res=1;    while(n) {        if(n&1) {            res=(1LL*res*x)%MOD;        }        x=(1LL*x*x)%MOD;        n>>=1;    }    return res;}void deal(int a,int b) {//得出dp[i][j]    if(a==b){        dp[a][b]=1;        return;    }    int res=fst(a-b,MOD-2);    dp[a][b]=(1LL*dp[a-1][b]*a)%MOD;    dp[a][b]=(1LL*dp[a][b]*res)%MOD;}int main() {    scanf("%d %d",&n,&k);    for(int i=1; i<=n; i++) scanf("%d",&A[i]);    sort(A+1,A+1+n);    for(int i=k; i<=n; i++) {        deal(i-1,k-1);        add(ans,1LL*dp[i-1][k-1]*A[i]);    }    printf("%d\n",ans);    return 0;}

WA:带模运算的组合数是不可以用除的,因为模了后不一定除得尽。

原创粉丝点击