codeforces 451E Devu and Flowers

版权声明:版权声明:本文为博主原创文章,未经博主允许不得转载,欢迎添加友链。 https://blog.csdn.net/zzk_233/article/details/83613231

1题目大意:给出n个盒子,每个盒子里有val[i]个球,想要拿出s个球,从每个盒子里拿出相同的球算同种方案,求方案数。

首先对于式子x_{1}+x_2+x_3+x_4+......x_n=s的非负整数解个数为C_{n+s-1}^{n-1},考虑隔板法,将s个东西分成n份,允许有空

相当于在s-1个空中放n-1个隔板,有空的盒子可以相当于先加上盒子个数个小球,真实计算是再减去,所以就是n+s个东西

其中n+s-1个空选择n-1个放。但是这道题有个数限制,所以不合法的情况就是其中的盒子放多了,因为放多了1个就不合法

所以设不合法的情况为val[i]+1(PS:之后再放自己里都不合法,所以不必计算,也不少不重复)。那么减去所有1个盒子放

多的情况时,多减去了2个放多的情况。所以要用容斥原理,减去奇数个放多的情况,对于组合数可以枚举,因为n≤20。

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define mode 1000000007
using namespace std;
typedef long long ll;
int n;
ll s,val[25],ans,now,gs,niv[25];
ll read()
{
    ll x=0,f=1;char ch;
    while(ch<'0'||ch>'9')  {if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return f*x;
}
ll C(ll x,ll y)
{
    if(x<y)return 0;
    ll as=1;
    for(ll i=x-y+1;i<=x;i++)
    {
        as=(as*(i%mode))%mode;
    }
    return as*niv[y]%mode;
}
int main()
{
    scanf("%d%lld",&n,&s);
    for(int i=1;i<=n;i++)
    {
        val[i]=read();
    }
    niv[1]=1;
    for(int i=2;i<=n;i++)niv[i]=(mode-mode/i)*niv[mode%i]%mode;
    niv[0]=1;
    for(int i=1;i<=n;i++)niv[i]=niv[i-1]*niv[i]%mode;
 	for(int i=0;i<=(1<<n)-1;i++)
 	{
 		gs=0,now=0; 
        for(int j=1;j<=n;j++)
        {
            if(1<<(j-1)&i)
            {
                gs++;now+=val[j]+1;
            }
        }	
        if(gs%2==0)ans+=C(s+n-now-1,n-1),ans%=mode;
        else ans-=C(s+n-now-1,n-1),ans%=mode;
    }
 	printf("%lld",(ans%mode+mode)%mode);
    return 0;
} 

猜你喜欢

转载自blog.csdn.net/zzk_233/article/details/83613231