CF451E Devu and Flowers (组合数学+容斥)

题目大意:给你$n$个箱子,每个箱子里有$a_{i}$个花,你最多取$s$个花,求所有取花的方案,$n<=20$,$s<=1e14$,$a_{i}<=1e12$

容斥入门题目

把取花想象成往箱子里放花,不能超过箱子上限

$n$很小,考虑状压

如果去掉$a_{i}$的限制,我们取物品的方案数是$C_{s+n-1}^{n-1}$,可以想象成$s+n-1$个小球,我们取出$n-1$个隔板,分隔出来的其他$n$个部分就是每个箱子里花的数量

但这样会算入不合法的方案,我们需要再去掉不合法的方案

假设我们要满足i合法,那么首先分配给$i$,$ai+1$朵花,来保证它是不合法的

然后,剩余$s+n-(ai+1)-1$朵花,我们仍然要分配给$n$个箱子,取出$n-1$个隔板,总方案数减去$C_{s+n-(ai+1)-1}^{n-1}$

然而我们由算入了一些情况,即$i$不合法,$j$也不合法$(j!=i)$,在计算$i$和计算$j$时都去掉了这种情况,我们还要把它加回来

总方案再加回来$C_{s+n-(ai+aj+2)-1}^{n-1}$

然后又多减掉了$i,j,k$都不合法....依次容斥即可

公式太长,也不好理解就不列了

这道题的组合数很大,不能直接求,我们需要一些优化

因为$n$很小,但$s$很大,所以上下化简掉阶乘里那一段特别长的部分,剩下的$O(n)$暴力计算就行了

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #define N 150
 5 #define uint unsigned int
 6 #define ll long long
 7 #define ull unsigned long long
 8 #define mod 1000000007
 9 using namespace std;
10 //re
11 /*int gint()
12 {
13     int ret=0,fh=1;char c=getchar();
14     while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();}
15     while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();}
16     return ret*fh;
17 }*/
18 int n;
19 ll sum,ma;
20 ll a[N],mu[N],inv[N],minv[N];
21 ll qpow(ll x,ll y){
22     ll ans=1;
23     if(y){
24         if(y&1) ans=ans*x%mod;
25         x=x*x%mod,y>>=1;
26     }return ans;
27 }
28 void Pre(){
29     minv[0]=minv[1]=inv[0]=inv[1]=1;
30     for(int i=2;i<=n;i++)
31         inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod,minv[i]=minv[i-1]*inv[i]%mod;
32 }
33 ll C(ll a,ll b)
34 {
35     ll ans=1;
36     if(b>a) return 0;
37     if(b==0) return 1;
38     for(ll j=a-b+1;j<=a;j++)
39         ans=j%mod*ans%mod;
40     ans=ans*minv[b]%mod;
41     return ans;
42 }
43 ll Lucas(ll a,ll b)
44 {
45     if(b>a) return 0;
46     if(a<mod&&b<mod) return C(a,b);
47     else return Lucas(a%mod,b%mod)*Lucas(a/mod,b/mod)%mod;
48 }
49 
50 int main()
51 {
52     //freopen("t1.in","r",stdin);
53     scanf("%d%I64d",&n,&ma);
54     int tot=(1<<n)-1;
55     for(int i=0;i<n;i++)
56         scanf("%I64d",&a[i]);
57     ll ans=0;
58     Pre();
59     for(int s=0;s<=tot;s++)
60     {
61         ll res=ma;int cnt=0;
62         for(int i=0;i<n;i++)
63             if(s&(1<<i)) res-=a[i]+1,cnt++; 
64         if(res<0) continue;
65         (ans+=1ll*(cnt&1?-1:1)*Lucas(res+n-1,n-1)%mod+mod)%=mod;
66     }
67     printf("%I64d\n",ans);
68     return 0;
69 }

 

posted @ 2018-10-31 19:37  guapisolo  阅读(279)  评论(1编辑  收藏  举报