[JSOI2011]分特产 题解

[JSOI2011]分特产 题解

题目地址:洛谷:暂无、BZOJ:Problem 4710. — [Jsoi2011]分特产

题目描述

JYY 带队参加了若干场ACM/ICPC 比赛,带回了许多土特产,要分给实验室的同学们。JYY 想知道,把这些特产分给N 个同学,一共有多少种不同的分法?当然,JYY 不希望任何一个同学因为没有拿到特产而感到失落,所以每个同学都必须至少分得一个特产。
例如,JYY 带来了2 袋麻花和1 袋包子,分给A 和B 两位同学,那么共有4 种不同的分配方法:
A:麻花,B:麻花、包子
A:麻花、麻花,B:包子
A:包子,B:麻花、麻花
A:麻花、包子,B:麻花

题意简述

有N个同学M种物品,每个物品有有限个,求每个同学都分到至少一个物品的方案数。

输入输出格式

输入格式:
输入数据第一行是同学的数量N 和特产的数量M。
第二行包含M 个整数,表示每一种特产的数量。
N, M 不超过1000,每一种特产的数量不超过1000

输出格式:
输出一行,不同分配方案的总数。由于输出结果可能非常巨大,你只需要输出最终结果MOD 1,000,000,007 的数值就可以了。

输入输出样例

输入样例#1:

5 4
1 3 3 5

输出样例#1:

384835

题解

首先我们会发现对于每个特产分开插板处理即可。插板解决一个可空分配方案数的答案是\mathrm{C}_{n+m-1}^{m-1}
考虑一个常规的容斥思路,即:
答案=至少0人没分到特产的方案数-至少1人没分到特产的方案数+至少2人没分到特产的方案数-……
为了保证“至少”,我们要钦定一些人不被分到,然后按照上面类似的插板思路分配剩下的人。因此最后的答案是
\sum_{i=0}^n \left[ (-1)^i \mathrm{C}_n^i \prod_{j=1}^m \mathrm{C}_{n+a_j-i-1}^{n-i-1} \right]

代码

// Code by KSkun, 2018/5
#include <cstdio>
#include <cctype>

#include <algorithm>

typedef long long LL;

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}

inline LL readint() {
    register LL res = 0, neg = 1;
    register char c = fgc();
    while(!isdigit(c)) {
        if(c == '-') neg = -1;
        c = fgc();
    }
    while(isdigit(c)) {
        res = (res << 1) + (res << 3) + c - '0';
        c = fgc();
    }
    return res * neg;
}

const int MAXN = 2005, MO = 1e9 + 7;

int n, m;
LL num[MAXN];

LL C[MAXN][MAXN];

inline void calc() {
    C[0][0] = 1;
    for(int i = 1; i < MAXN; i++) {
        C[i][0] = 1;
        for(int j = 1; j <= i; j++) {
            C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % MO;
        }
    }
}

int main() {
    calc();
    n = readint(); m = readint();
    for(int i = 1; i <= m; i++) {
        num[i] = readint();
    }
    LL ans = 0, t = -1;
    for(int i = 0; i <= n; i++) {
        t *= -1;
        LL res = 1;
        for(int j = 1; j <= m; j++) {
            res = res * C[n + num[j] - i - 1][n - i - 1] % MO;
        }
        ans = (ans + t * C[n][i] * res % MO) % MO;
    }
    printf("%lld", (ans + MO) % MO);
    return 0;
}


发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据