1. 程式人生 > >CodeForces - 451E Devu and Flowers【簡單容斥】

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