CodeForces - 451E Devu and Flowers【简单容斥】

题意:给N个数(a1,a2……an)和一个S,求(b1+b2+……+bn)==S的方案数,其中0<=bi<=ai;

分析:题意很简单,考虑容斥。

ans=C_{s+n-1}^{n-1}-$$\sum_{i=1}^n{(-1)^{i}\sum_{t\subseteq U}{C_{s-sum+n+1}^{n-1}}}$$        sum=\sum _{i\subseteq t}{a_i+1}

其中U为全集,t为U的i元组。

由于N<=20,直接二进制枚举即可,s很大,考虑使用lucas优化。

#include <bits/stdc++.h>
using namespace std;
const int mod = 1e9+7;
long long f[30];
long long qk(long long a,long long n)
{
    long long ans=1;
    while(n){
        if(n&1)ans=ans*a%mod;
        a=a*a%mod;
        n>>=1;
    }
    return ans;
}
long long c(long long a,long long b)
{
    if(a==b)return 1;
    if(a<b)return 0;
    long long up=1,dw=1;
    for (int i = a-b+1; i <= a; ++i) {
        up=up*i%mod;
    }
    for (int i = 2; i <= b; ++i) {
        dw=dw*i%mod;
    }
    return up*qk(dw,mod-2)%mod;
}
long long lucas(long long a,long long b)
{
    if(a<mod&&b<mod)
    {
        return c(a,b);
    }
    else
    {
        return (c(a%mod,b%mod)*lucas(a/mod,b/mod))%mod;
    }
}
int main () {
    long long n,s;
    long long ans=1;
    cin>>n>>s;
    for (int i = 0; i < n; ++i) {
        scanf("%lld",&f[i]);
    }
    ans=lucas(s+n-1,n-1);//总方案数
    int len=1<<n;
    //二进制枚举,减去不合法方案
    for (int i = 1; i < len; ++i) {
        long long sum=0;
        long long cnt=0;
        for (int j = 0; j < n; ++j) {
            int x=1<<j;
            if(x&i)
            {
                cnt++;
                sum+=f[j]+1;
            }
        }
        if(cnt&1)
        {
            ans=(ans-lucas(s-sum+n-1,n-1)+mod)%mod;
        }
        else
        {
            ans=(ans+lucas(s-sum+n-1,n-1))%mod;
        }
    }
    printf("%lld\n",ans);
}


猜你喜欢

转载自blog.csdn.net/qq_42671946/article/details/85064509
今日推荐