[CF451E] Devu and Flowers
这道题要求的方案数,等价于s个小球(小球相同)、n个盒子(盒子不同)、允许有空盒子的放球的方案数。
如果没有f[i]的限制,方案数是C(s+n-1,n-1)。
考虑用总方案数-不合法方案数求出合法方案数。
可以用一下状压的思想,如果状态某一位是1就代表该处超过了f的限制。
这样知道了哪几个盒子超出容量了,超出的盒子至少装了f+1个球,先从s里面把f+1减掉。
剩下的球再往里装的时候,就不受f的限制了。
超出容量的盒子理应装多于f个,但是我们预先减掉了f+1,所以接下来装多少都行了,因为减掉的f+1已经保证这个盒子超出容量了。
但是我们这么算会有重复情况,比如说某状态代表1、2两个盒子超出容量了,但是不能保证1和2以外的盒子不超出容量。
所以1、2的这个状态不是代表1和2超出容量,而是代表至少有1和2超出容量,别的盒子不能保证不超。
而1 2 3超出容量的、1 2 3 4超出容量的......,等等这些,之后还会再被算一次。
所以再用一下容斥原理即可。
顺便提一下,这道题模数比较大,不能线性预处理逆元和阶乘了。
算组合数的时候,比较大的用卢卡斯定理拆开,小的就暴力算。
1 #include<cstdio> 2 typedef long long ll; 3 const int mod=1000000007; 4 int n; 5 ll s; 6 ll f[25]; 7 8 ll ksm(ll b,int p) 9 { 10 ll ret=1; 11 while(p) 12 { 13 if(p&1)ret=(ret*b)%mod; 14 b=(b*b)%mod; 15 p>>=1; 16 } 17 return ret; 18 } 19 20 ll c(ll cn,ll cm) 21 { 22 if(cm>cn)return 0; 23 if(cn>=mod) 24 return c(cn%mod,cm%mod)*c(cn/mod,cm/mod)%mod; 25 if(cm>cn-cm)cm=cn-cm; 26 ll ca=1,cb=1; 27 for(ll i=1;i<=cm;i++) 28 ca=ca*(cn-i+1)%mod,cb=cb*i%mod; 29 return ca*ksm(cb,mod-2)%mod; 30 } 31 32 int main() 33 { 34 scanf("%d%I64d",&n,&s); 35 for(int i=1;i<=n;i++)scanf("%I64d",&f[i]); 36 ll ans=0; 37 for(int i=0;i<(1<<n);i++) 38 { 39 int fl=1,t=i; 40 ll tot=s; 41 for(int j=1;j<=n;j++) 42 { 43 if(i&(1<<(j-1))) 44 { 45 tot-=(f[j]+1); 46 fl=-fl; 47 } 48 } 49 if(tot<0)continue; 50 ans=((ans+c(tot+n-1,n-1)%mod*fl)%mod+mod)%mod; 51 } 52 printf("%I64d",ans); 53 return 0; 54 }