• CF451E Devu and Flowers


    Desciption

    求一个可重集 (S)(m) 子集的个数。(S) 的不同元素个数为 (n)(nleq 20)。每种元素的个数 (a_ileq 10^{12}) 。答案对 (10^9+7) 取模。

    Solution

    如果每种元素都有无数种的话,那么直接用插板法可得方案数为 (inom{m+n-1}{n-1})。但是问题是每种元素个数都有一个上界,这样会得到一些不合法的方案。考虑将其减去。令 (f_{sta}) 表示至少是元素集合为 (sta) 的所有元素都超上界的方案,其中 (sta) 是一个二进制状态,也是一个集合。那么有

    [f_{sta}=inom{m+n-1-sum_{iin sta} (a_i+1)}{n-1} ]

    再令 (g_{sta}) 表示恰好是集合 (sta) 里的元素超上界的方案,由容斥原理得

    [g(S)=sum_{Ssubseteq T} (-1)^{|T|-|S|} f(T) ]

    我们要求的答案就是

    [g(emptyset)=sum_{T} (-1)^{|T|} f(T) ]

    所以只需要快速求出 (f(T))(f(T)) 中上标很大,但下标很小,所以中间约掉后,总共只需要算十几次。还要注意,如果直接将那几个大数乘在一起的话,即使是取模也会爆 long long,正确的做法是用 Lucas 定理将上标化小。

    [inom{n}{m}equiv inom{n/p}{m/p} imes inom{n mod p}{m mod p} equiv inom{n mod p}{m} ]

    复杂度 (O(n2^n))

    #include<stdio.h>
    #define ll long long
    
    const int N=20;
    const int Mod=1e9+7;
    
    ll a[N],inv[23];
    
    ll C(ll n,ll m){
        n%=Mod;
        if(n<m) return 0;
        ll ret=1;
        for(ll i=1;i<=m;i++) ret=ret*inv[i]%Mod;
        for(ll i=n-m+1;i<=n;i++) ret=ret*i%Mod;
        return ret;
    }
    
    int n;
    ll ans=0,m;
    
    void dfs(int st,int op,ll now){
        if(st==n){ans=(ans+op*C(now,n-1)%Mod+Mod)%Mod;return;}
        dfs(st+1,op,now); 
        if(now-a[st]-1<0) return;
        dfs(st+1,-op,now-a[st]-1);
    }
    
    int main(){
        scanf("%d%lld",&n,&m);
        for(int i=0;i<n;i++) scanf("%lld",&a[i]);
        inv[1]=1; for(int i=2;i<20;i++) inv[i]=(Mod-Mod/i)*inv[Mod%i]%Mod;
        dfs(0,1,n+m-1);
        printf("%lld",ans);
    }
    
  • 相关阅读:
    Javascript:设计模式策略模式
    list中元素的插入与使用 scholarfor
    字符串常用命令 scholarfor
    2_5_zen scholarfor
    5_6 人生的不同阶段 scholarfor
    条件测试:列表中的元素为某一特定值(audi)的话全大写,其他元素首字母大写 scholarfor
    字符串的遍历 scholarfor
    5.2 条件测试,遍历字典里面的value值 scholarfor
    5.4 if in 语句 与 for in 语句 处理元素在列表中 scholarfor
    UBoot学习
  • 原文地址:https://www.cnblogs.com/wwlwQWQ/p/14897534.html
Copyright © 2020-2023  润新知