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;
}

容斥版

咕咕咕

posted @ 2019-03-12 17:21  dreagonm  阅读(204)  评论(0编辑  收藏  举报