[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 }
原文地址:https://www.cnblogs.com/eternhope/p/9887210.html