1. 程式人生 > >CF451E Devu and Flowers (組合數學+容斥)

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 }