D. Time to go back(思维)
题目链接:http://codeforces.com/gym/100952/problem/D
题目大意:n个礼物,m个人,要给m个人中的k个人买大于等于d的礼物,其他人随意,问你选择礼物的方案数(不是分配礼物的方案数)。
具体思路:一开始我的思路,先输出大于等于k的礼物的个数ans,然后再直接计算C(ans,k)*C(n-ans,m-k)就可以了。但是这样会出现重复计算的情况。比如说,4,5,6,7都满足情况,如果是按照我的思路的话,(4,5,6)和(4,6,5)是不同的,但是我们求的是选择礼物的方案数,不是分配数,所以这两个属于一个方案。然后就问了一下别人的思路,我们每一次选取t(t>=k)个满足大于d的值,然后剩下的从小于d的里面选,当t到达大于d的个数的时候,或者人数够了的时候停止,这样就能保证每一种方案里面,大于d的个数就肯定不同了。
感谢张明学长和lhk的精彩解释。
AC代码:
1 #include<iostream> 2 #include<stdio.h> 3 #include<cmath> 4 using namespace std; 5 # define ll long long 6 const int maxn =2e5+100; 7 const int mod = 1e9+7; 8 ll a[maxn]; 9 ll tmp[maxn]; 10 ll quickpow(ll t1,ll t2){ 11 ll ans=1; 12 while(t2){ 13 if(t2&1)ans=ans*t1%mod; 14 t2>>=1; 15 t1=t1*t1%mod; 16 } 17 return ans; 18 } 19 ll C(ll n,ll r){ 20 if(n==r||r==0)return 1; 21 tmp[0]=1; 22 for(ll i=1;i<=r;i++){ 23 tmp[i]=(tmp[i-1]*(n-i+1)%mod*quickpow(i,mod-2))%mod; 24 } 25 return tmp[r]%mod; 26 } 27 int main(){ 28 int T; 29 scanf("%d",&T); 30 while(T--){ 31 ll n,m,k,d; 32 scanf("%lld %lld %lld %lld",&n,&m,&k,&d); 33 ll ans=0; 34 for(ll i=1;i<=n;i++){ 35 scanf("%lld",&a[i]); 36 if(a[i]>=d)ans++; 37 } 38 ll w=min(m,ans);//注意取min 39 ll tot=0; 40 for(ll i=k;i<=w;i++){ 41 tot+=C(ans,i)*C(n-ans,m-i)%mod; 42 tot%=mod; 43 } 44 printf("%lld\n",tot%mod); 45 } 46 return 0; 47 }