51nod 1453 抽彩球 插板法(组合数学)

来源:互联网 发布:游戏制作程序软件 编辑:程序博客网 时间:2024/04/28 00:03

一个袋子中有n个彩球,他们用k种不同的颜色染色。颜色被从1到k编号。同一种颜色的球看成是一样的。现在从袋中一个一个的拿出球来,直到拿完所有的球。对于所有颜色为i (1<=i<=k-1)的球,他的最后一个球总是在编号比他大的球拿完之前拿完,问这样情况有多少种。

样例解释:这个样例中有2个1号颜色的球,2个2号颜色的球,1个3号颜色的球。三种方案是:
1 2 1 2 3
1 1 2 2 3
2 1 1 2 3


Input
单组测试数据。第一行给出一个整数k(1 ≤ k ≤ 1000),表示球的种类。接下来k行,每行一个整数ci,表示第i种颜色的球有ci个(1 ≤ ci ≤ 1000)。球的总数目不超过1000。
Output
输出总数对1,000,000,007的模即可。
Input示例
3221
Output示例
3
System Message (题目提供者)
C++的运行时限为:1000 ms ,空间限制为:131072 KB 示例及语言说明请按这里
 允许其他 AC 的用户查看此代码,分享代码才能查看别人的代码并有机会获得勋章

运用了组合数学插空法的知识
https://baike.baidu.com/item/%E6%8F%92%E7%A9%BA%E6%B3%95/4862293?fr=aladdin

对于每种球,只有一个要求,那就是每种球最后一个是固定的,只其余的求可以随便放。
所以我们倒着来。算计算一共有多少球,sum个。
最后一种颜色的最后一个球肯定放在最后
我们只需要在前sum-1个空位随便选择a[i]个位置把这些球放进去即可。 C(sum-1,a[i]);
之后让sum-=a[i]。代表着空位少了a[i]个了。
同理,继续对于下一个球,最后一个球固定,放在sum位置,从前sum-1个位置找a[i]个放进入 C(sum-1,a[i])。

利用逆原来处理组合数即可。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
int n;
int a[1100];
long long fac[1100];
const int MOD=1000000007;
//////////////////////////////////////////////////////////////////
//求组合数
long long ppow(long long x, long long y)
{
    long long c = 1;
    while (y)
    {
        if (y & 1)
        {
            c = c * x % MOD;
        }
        y >>= 1;
        x = x * x % MOD;
    }
    return c;
}


long long C(long long m, long long i)
{
    return ((fac[m] % MOD) * (ppow((fac[i] * fac[m-i]) % MOD, MOD - 2) % MOD)) % MOD;
}
void init()
{
    fac[0] = 1;
    for(int i = 1; i < 1000; i++)
    {
        fac[i] = (fac[i - 1] * i) % MOD;
    }
}
///////////////////////////////////////////////////////////////////////////////////////////




int main()
{
    init();
    long long ans = 1,sum = 0;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>a[i];
        sum+=a[i];
    }
    for(int i=n-1;i>=0;i--)
    {
        ans=ans*C(sum-1,a[i]-1);
        ans=ans%MOD;
        sum-=a[i];
    }
    cout<<ans<<endl;


}
原创粉丝点击