题意 :
就是给一组数 ,让求选其中的k个数组合出最大的值之和 , 问有多少种组合方法 。
思路
提交的时候WA了好多发 ,第一次知道组合数大的时候需要用费马小定理求组合数 ,否则会错 /(ㄒoㄒ)/~~ 。 先说原理 :
为什么组合数取模要用逆元
首先说明一个事实,你直接算出来一个组合数的结果直接对p取模,结果一定是对的,那么这是对一个计算结果一次取模(但上面的前提是你使用的数据结构能存储得下取模前的结果
但如果我们要通过一个前面取过模的式子递推出其他要取模的式子,而递推式里又存在除法
那么一个很尴尬的事情出现了,假如a[i-1]=100%31=7 a[i]=(a[i-1]/2)%31
a[i]=50%31=19 ,但我们现在只知道a[i-1]=7,如何计算出a[i]=19呢? a[i]=(7/2)%31=3?
其实本来是100是整除2的,但是对31取模后就不能整除了,所以我们要求出在mod 31意义下2的逆元是多少
口算可得,2*16%31=1,所以2的逆元就是16,所以a[i]=(a[i-1]inv(2))%31=716%31=19
那么通过逆元我们就得到了正确的结果
回到该题 , 首先先用桶排统计每个数出现的次数 ,因为数据范围也不大 ,懒得用vector了 ,例如1 2 2 3 3 4 , 需要选择4个数 , 那么先从大到小选择,前面的数都是固定的 , 选择到2 发现有两个2 , 但是此时只需要选择1个数 ,则答案即为(C2,1) ,注意判断特殊情况就行了。
代码 :
#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;
const int mod = 1e9 + 7 ;
int a[1010] , b[1010] ;
int n , k , t ;
inline ll qpow(ll a , ll b , ll modd){ //快速幂
ll ans = 1 ;
while(b){
if(b & 1) ans = ans * a % modd ;
a = a * a % modd ;
b >>= 1 ;
}
return ans ;
}
inline ll C(ll n , ll m){ //组合数Cnm的值
ll ans1 = 1 , ans2 = 1 , ans3 = 1 ;
for(ll i = 1 ; i <= n ; i++)
ans1 = ans1 * i % mod ;
for(ll i = 1 ; i <= m ; i++)
ans2 = ans2 * i % mod ;
for(ll i = 1 ; i <= n - m ; i++)
ans3 = ans3 * i % mod ;
return ans1 * qpow(ans2 , mod - 2 , mod) % mod * qpow(ans3 , mod - 2 , mod) % mod ;
//费马小定理
}
int main(){
cin >> t ;
while(t--){
cin >> n >> k ;
memset(a , 0 , sizeof(a)) ;
memset(b , 0 , sizeof(b)) ;
for(int i = 1 ; i <= n ; i++){
cin >> a[i] ;
b[a[i]]++ ;
}
if(k == n){ //相等 , 只有一个
cout << 1 << endl ;
continue ;
}
ll sum = 0 ;
int id = 0 ;
sort(a + 1 , a + 1 + n) ;
ll cnt = 0 ; //cnt表示前面选择的多少个数是固定的
for(int i = n ; i >= 1 ; i--){
if(cnt + b[a[i]] < k){
cnt += b[a[i]] ;
while(a[i] == a[i-1]) i-- ;
}
else{
id = i ;
break ;
}
}
if(cnt + b[a[id]] == k){
cout << 1 << endl ;
continue ;
}
else{
int temp = k - cnt ; //temp表示还剩下几个数要选
cout << C(b[a[id]] , temp) % mod << endl ;
}
}
return 0 ;
}
ps : 上分局 , 打到一半 , 系统貌似崩溃了 , CF说不算分了 , 心态崩了 555 /(ㄒoㄒ)/~~