CF451E Devu and Flowers
思路
母函数
题目等价于求这样的一个问题
给出n个变量\(x_1,x_2,\dots,x_n\),求方程\(x_1+x_2+x_3+\dots+x_n=S\)的整数解方案数且要满足\(x_1\le f_1.x_2\le f_2,\dots,x_n\le f_n\)
乍一看不是很好做,那怎么办?
看到\(n\le20\),状压?其实生成函数也可以做
写出整个的生成函数
\[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(\begin{matrix}n+i-1\\n-1\end{matrix}\right)x^i
\]
前面的暴力展开,最多有\(2^n\)项且系数只会有1或-1
考虑后面的项怎么做
假设现在求出了第k项,我们需要在后面的式子的第s-k项系数(如果k>s,忽略即可)
第s-k项的系数就是\(\left(\begin{matrix}n+s-k-1\\n-1\end{matrix}\right)\),但是s有\(10^{14}\),我们还需要变形一下
\[\begin{align}&\left(\begin{matrix}n+s-k-1\\n-1\end{matrix}\right)\\=&\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(\begin{matrix}s-m+n-1\\n-1\end{matrix}\right)\)
这样显然相当于是至少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\n",ans);
return 0;
}
容斥版
咕咕咕