题目:CF451E.
题目大意:给定
n
n
n种花,第
i
i
i种花有
A
i
A_i
Ai束,现在要选出
m
m
m束花,问方案数.
1
≤
n
≤
20
,
0
≤
m
≤
1
0
14
,
0
≤
A
i
≤
1
0
12
1\leq n\leq 20,0\leq m\leq 10^{14},0\leq A_i\leq 10^{12}
1≤n≤20,0≤m≤1014,0≤Ai≤1012.
我们可以知道如果 m ≤ A i m\leq A_i m≤Ai,那么方案数就是 C n − 1 n + m − 1 C_{n-1}^{n+m-1} Cn−1n+m−1.
如果 m > A i m>A_i m>Ai,那么我们就需要把所有方案中有任意一种花 i i i选超过了 A i A_i Ai束的全部去掉.
考虑对于任意一个 i i i,先选入 A i + 1 A_i+1 Ai+1束第 i i i种花,剩下还有 C n − 1 n + m − A i − 2 C_{n-1}^{n+m-A_i-2} Cn−1n+m−Ai−2中方案需要减去.然后发现这里面又有重复的,即有两种花 i , j i,j i,j都超过的,那么这样子的有方案数 C n − 1 n + m − A i − A j − 3 C_{n-1}^{n+m-A_i-A_j-3} Cn−1n+m−Ai−Aj−3需要加上.以此类推.
具体如何实现只要 O ( 2 n ) O(2^n) O(2n)枚举每一个数,然后计算方案数,设选中的数为 k k k,则这一项的系数为 ( − 1 ) k (-1)^k (−1)k,并且同时计算组合数.不过由于组合数过大,需要用到Lucas把 n + m − 1 n+m-1 n+m−1对模数取模,而且组合数中下面部分太大,上面部分却很小,所以要用 O ( m ) O(m) O(m)复杂度递推 C n m C_{n}^{m} Cnm.
时间复杂度 O ( n 2 n ) O(n2^n) O(n2n).
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=20;
const LL mod=1000000007;
int n;
LL m,a[N+9],inv[N+9],ans=0;
void pre_inv(int n){
inv[1]=1;
for (int i=2;i<=n;++i)
inv[i]=(mod-mod/i)*inv[mod%i]%mod;
}
LL C(LL n,LL m){
if (n<0||m<0||n<m) return 0;
n%=mod;
LL ans=1;
for (LL i=1;i<=m;++i)
ans=ans*inv[i]%mod*((n-i+1)%mod)%mod;
return ans;
}
Abigail into(){
scanf("%d%I64d",&n,&m);
for (int i=1;i<=n;++i)
scanf("%I64d",&a[i]);
}
Abigail work(){
LL t,k;
pre_inv(n);
for (int i=0;i<1<<n;++i){
t=n+m-1;k=1;
for (int j=1;j<=n;++j)
if (i>>j-1&1) t-=a[j]+1,k=-k;
ans+=k*C(t,(LL)n-1);
ans=(ans%mod+mod)%mod;
}
}
Abigail outo(){
printf("%I64d\n",ans);
}
int main(){
into();
work();
outo();
return 0;
}