题目大意
给你n个数,选出其中的k个数,使这k个数的和最大,求总共有多少种选法,满足题目的要求。
思路
先对这n个数进行从大到小的排序,找到前k大的n个数,为了保证和是最大的,只能改变k个数中最小的数MINNUM的选择情况,因为比MINNUM 大的数会被全部选中,即会出现
C
n
n
=
1
,
C_{n}^{n}= 1,
Cnn=1,的情况,因此只有最后一个数与和它等值的数在k个数中与n个数中的分布情况,会决定最后的答案。
假设在k个数中,共有M个值为MINNUM,在n个数中,有N个值为MINNUM,即可得
数学公式
C
N
M
C_{N}^{M}
CNM
该值也就是最后的答案。
重要方法
为了在n较大的情况下(n<=1000)下算得
C
N
M
C_{N}^{M}
CNM需要使用快速计算组合数的模板
该模板如下
const int MOD = 1e9+7;
ll fac[N];//储存除模后的阶乘的值
ll Mode(ll a, ll b, ll mode)//快速幂
{
ll sum = 1;
a = a % mode;
while (b > 0)
{
if (b % 2 == 1) //判断是否是奇数,是奇数的话将多出来的数事先乘如sum
{
sum = (sum * a) % mode;
}
b /= 2;
a = (a * a) % mode;
// 不断的两两合并再取模,减小a和b的规模
}
return sum%mode;
}
ll C(ll m,ll n)//快速计算组合数
{
return fac[n]*Mode(fac[m]*fac[n-m]%MOD,MOD-2,MOD)%MOD;
}
完整代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#define fast ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define ll long long
using namespace std;
bool cmp(ll a,ll b)
{
return a > b;
}
const int N = 1e3+5;
const int MOD = 1e9+7;
ll a[N], fac[N];
ll Mode(ll a, ll b, ll mode)
{
ll sum = 1;
a = a % mode;
while (b > 0)
{
if (b % 2 == 1) //判断是否是奇数,是奇数的话将多出来的数事先乘如sum
{
sum = (sum * a) % mode;
}
b /= 2;
a = (a * a) % mode;
// 不断的两两合并再取模,减小a和b的规模
}
return sum%mode;
}
ll C(ll m,ll n)
{
return fac[n]*Mode(fac[m]*fac[n-m]%MOD,MOD-2,MOD)%MOD;
}
int main()
{
int t;
scanf("%d",&t);
fac[0] = 1;
for(int i=1;i<=1000;++i)//初始化阶乘的值
{
fac[i] = fac[i-1]*i%MOD;
}
while(t--)
{
int n, k;
scanf("%d %d",&n,&k);
for(int i=1;i<=n;++i)
{
scanf("%lld",&a[i]);
}
sort(a+1,a+n+1,cmp);
int x = 1, y = 0;
for(int i=k-1;i>=1;--i)
{
if(a[k]==a[i])
{
++x;
}
else
{
break;
}
}
for(int i=k+1;i<=n;++i)
{
if(a[k]==a[i])
{
++y;
}
else
{
break;
}
}
ll M = x, N = x + y;
printf("%lld\n",C(M,N));
}
return 0;
}