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
",tot%mod);
45 }
46 return 0;
47 }
原文地址:https://www.cnblogs.com/letlifestop/p/10503093.html