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 }