思路
母函数
题目等价于求这样的一个问题
给出n个变量(x_1,x_2,dots,x_n),求方程(x_1+x_2+x_3+dots+x_n=S)的整数解方案数且要满足(x_1le f_1.x_2le f_2,dots,x_nle f_n)
乍一看不是很好做,那怎么办?
看到(nle20),状压?其实生成函数也可以做
写出整个的生成函数
[G(x)=(1+x+x^2+dots+x^{f_1})(1+x+x^2+dots+x^{f_2})dots(1+x+x^2+dots+x^{f_n})
]
通过等比数列求和公式,可以化成这样
[G(x)=frac{(1-x^{f_1+1})(1-x^{f_2+1})dots (1-x^{f_n+1})}{(1-x)^n}
]
答案就是第s项系数
然后通过生成函数的公式
[G(x)= (1-x^{f_1+1})(1-x^{f_2+1})dots (1-x^{f_n+1})sum_{i=0}^{infty}left(egin{matrix}n+i-1\n-1end{matrix}
ight)x^i
]
前面的暴力展开,最多有(2^n)项且系数只会有1或-1
考虑后面的项怎么做
假设现在求出了第k项,我们需要在后面的式子的第s-k项系数(如果k>s,忽略即可)
第s-k项的系数就是(left(egin{matrix}n+s-k-1\n-1end{matrix} ight)),但是s有(10^{14}),我们还需要变形一下
[egin{align}&left(egin{matrix}n+s-k-1\n-1end{matrix}
ight)\=&frac{(n+s-k-1)!}{(n-1)!(s-k)!}\=&frac{prod_{i=s-k+1}^{n+s-k-1}i}{(n-1)!}end{align}
]
然后就相当可做了
容斥
看到20,想到状压
2^n枚举那几个x超过限制,则它们至少都有(f_x+1)个,从s中减去这些,剩下的随意分配,就相当于求方程(x_1+x_2+dots+x_n=s)的整数解个数,利用组合数学的知识,用0的个数表示每个x的大小,相当于加入n-1个1分割0的方案数,或者是多重集合({0·s,1*(k-1)})的排列数,就是(left(egin{matrix}s-m+n-1\n-1end{matrix}
ight))
这样显然相当于是至少x个不超过限制,其他没有限制,所以枚举子集容斥即可
其实最后的式子两个一模一样
代码
母函数版
#include <set>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define int long long
const int MOD = 1000000007;
using namespace std;
int jcn_1=1,inv_jcn_1,n,f[100],s,ans=0;
int pow(int a,int b){
int ans=1;
a%=MOD;
while(b){
if(b&1)
ans=(ans*a)%MOD;
a=(a*a)%MOD;
b>>=1;
}
return ans;
}
int C(int k){//k
int ans=1;
for(int i=s-k+1;i<=n+s-k-1;i++)
ans=(ans*(i%MOD))%MOD;
return ans*inv_jcn_1%MOD;
}//return c(x,n-1)
void dfs(int pos,int opt,int x){
if(pos==n){
if(x>s)
return;
ans=((ans+opt*C(x))%MOD+MOD)%MOD;
return;
}
dfs(pos+1,opt,x);
dfs(pos+1,-opt,x+f[pos+1]+1);
}
signed main(){
scanf("%I64d %I64d",&n,&s);
for(int i=1;i<n;i++)
jcn_1*=i;
inv_jcn_1=pow(jcn_1,MOD-2);
for(int i=1;i<=n;i++)
scanf("%I64d",&f[i]);
dfs(0,1,0);
printf("%I64d
",ans);
return 0;
}
容斥版
咕咕咕