洛谷题单指南-组合数学与计数-P5505 [JSOI2011] 分特产

原题链接:https://www.luogu.com.cn/problem/P5505

题意解读:有n种颜色的球,每种颜色有a[i]个相同的球,分成m组,每组至少有一个球的方案数。注意n和m与题目中描述相反。

解题思路:

直接计数比较困难,通过补集的角度来考虑。

1、先不考虑每组至少有一个球,所有方案

对于n中颜色的球,一个一个颜色来考虑,对于某个颜色i的a[i]个球,要任意分成m组,借助于插空法的变形,

先借m个球一共a[i]+m个球,在a[i]+m-1个空插m-1板即可,一共是C(a[i]+m-1,m-1)种。

那么对于所有颜色,根据乘法原理,一共有

image

2、再考虑有1组为空的方案数,应该减掉

仍然一个一个颜色来考虑,先把空的一组选出来C(m,1),再插板,一共是C(m,1)(C(a[i]+m-2,m-2)种。

对于所有颜色,一共有

image

3、再考虑有2组为空的方案数,应该加上

一共有

image

4、以此类推,一直到m-1组为空的情况。

最终,答案是

image

这,又是容斥原理!

进一步转化为:

image

组合数可以预计算,整体复杂度为O(nm)。

100分代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

const int N = 2005, MOD = 1e9 + 7;
int a[N], c[N][N];
int m, n, ans;

int main()
{
    cin >> m >> n;
    for(int i = 1; i <= n; i++) cin >> a[i];

    //组合数初始化
    for(int i = 0; i <= 2000; i++)
    {
        for(int j = 0; j <= i; j++)
        {
            if(j == 0) c[i][j] = 1;
            else c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % MOD;
        }
    }

    for(int j = 0; j <= m - 1; j++)
    {
        int tmp = c[m][j];
        for(int i = 1; i <= n; i++)
        {
            tmp = 1ll * tmp * c[a[i] + m - j - 1][m - j - 1] % MOD;
        }
        if(j % 2 == 0) ans = (ans + tmp) % MOD;
        else ans = (ans - tmp + MOD) % MOD;
    }
    cout << ans;
    return 0;
}

 

posted @ 2025-11-25 11:41  hackerchef  阅读(6)  评论(0)    收藏  举报