zoukankan      html  css  js  c++  java
  • [JSOI2011]分特产

    可以发现这样一件事情,不同种类的特产之间对方案是不会有影响的,因此我们可以每次考虑将一种特产分配给所有人。于是就有了一个 (dp),令 (dp_{i, j}) 表示考虑完前 (i) 种特产,已经有 (j) 个人分配到了特产的方案。转移时考虑当前新增几个人获得特产,先将当前特产给这些人每个分配 (1) 再考虑将这些特产分配给当前 (j) 个人,因为同种特产都是一样的,两个方案不同当前仅当存在某个人分配的数量不同,这相当于问方程 (x_1 + x_2 + cdots x_j = k) 的解非负整数解数量,直接使用插板法解决,形式化地就是(令第 (i) 种特产有 (a_i) 个):

    [dp_{i, j} = sumlimits_{k = 0} ^ {min(a_i, j)} dbinom{m - j + k}{k} imes dbinom{a_i - k + j - 1}{j - 1} dp_{i - 1, j - k} ]

    (dp_{m, n}) 即为答案。

    #include<bits/stdc++.h>
    using namespace std;
    #define rep(i, l, r) for(int i = l; i <= r; ++i)
    const int N = 1000 + 5;
    const int Mod = 1000000000 + 7;
    int n, m, a[N], dp[N][N], C[N * 2][N * 2];
    int read(){
        char c; int x = 0, f = 1;
        c = getchar();
        while(c > '9' || c < '0'){ if(c == '-') f = -1; c = getchar();}
        while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
        return x * f;
    }
    int Inc(int a, int b){
        return (a += b) >= Mod ? a - Mod : a;
    }
    int Mul(int a, int b){
        return 1ll * a * b % Mod;
    }
    int main(){
        n = read(), m = read();
        rep(i, 1, m) a[i] = read();
        rep(i, 0, (N - 5) * 2) C[i][0] = 1;
        rep(i, 1, (N - 5) * 2) rep(j, 1, i) C[i][j] = Inc(C[i - 1][j - 1], C[i - 1][j]);
        dp[0][0] = 1, random_shuffle(a + 1, a + m + 1);
        rep(i, 1, m) rep(j, 1, n) rep(k, 0, min(j, a[i])){
            dp[i][j] = Inc(dp[i][j], Mul(Mul(C[n - j + k][k], C[a[i] - k + j - 1][j - 1]), dp[i - 1][j - k]));
        }
        printf("%d", dp[m][n]);
        return 0;
    }
    

    但是上面这个 (dp) 还是 (O(n ^ 3)) 的,还不能通过本题。可以发现上面这个 (dp) 实际上是逐步确保每个人都有特产可选的过程,本质上就是恰好有 (n) 个人都有特产的方案。对于这种恰好,我们可以考虑使用二项式反演(也就是容斥)。但是会发现这样一个问题,钦定有 (i) 个位置一定有特产的方案实际上又是原来的问题,这时我们的一般思路就是反过来考虑求恰好有 (0) 个位置满足没有特产,因为没有特产是很好保证的。于是我们可以令 (f_i) 表示钦定有 (i) 个位置没有特产的方案,则:

    [f_i = dbinom{n}{i} prodlimits_{j = 1} ^ m dbinom{a_i + n - i - 1}{n - i - 1} ]

    (g_i) 表示恰好有 (i) 个位置没有特产的方案,那么可以推导出 (f, g) 的关系:

    [f_i = sumlimits_{j = i} ^ n dbinom{j}{i} g_j ]

    则:

    [g_0 = sumlimits_{i = 0} ^ n (-1) ^ i dbinom{n}{i} prodlimits_{j = 1} ^ m dbinom{a_i + n - i - 1}{n - i - 1} ]

    直接计算即可。

    #include<bits/stdc++.h>
    using namespace std;
    #define rep(i, l, r) for(int i = l; i <= r; ++i)
    const int N = 2000 + 5;
    const int Mod = 1000000000 + 7;
    int n, m, ans, tmp, a[N], C[N][N];
    int read(){
        char c; int x = 0, f = 1;
        while(c > '9' || c < '0'){ if(c == '-') f = -1; c = getchar();}
        while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
        return x * f;
    }
    int Inc(int a, int b){
        return (a += b) >= Mod ? a - Mod : a;
    }
    int Dec(int a, int b){
        return (a -= b) < 0 ? a + Mod : a;
    }
    int Mul(int a, int b){
        return 1ll * a * b % Mod;
    }
    int main(){
        n = read(), m = read();
        rep(i, 1, m) a[i] = read();
        rep(i, 0, N - 5) C[i][0] = 1;
        rep(i, 1, N - 5) rep(j, 1, i) C[i][j] = Inc(C[i - 1][j - 1], C[i - 1][j]);
        rep(i, 0, n){
            tmp = 1;
            rep(j, 1, m) tmp = Mul(tmp, C[a[j] + n - i - 1][n - i - 1]);
            if(i & 1) ans = Dec(ans, Mul(tmp, C[n][i]));
            else ans = Inc(ans, Mul(tmp, C[n][i]));
        }
        printf("%d", ans);
        return 0;
    }
    
    GO!
  • 相关阅读:
    一本通1268 完全背包问题
    一本通1267 01背包
    合并石子1,2
    求最长不下降子序列++
    数字金字塔升级版
    一本通1354 括弧匹配检验
    一本通1353表达式括号匹配
    一本通1357车厢调度
    Centos查看端口占用情况和开启端口命令
    centos后台运行python程序
  • 原文地址:https://www.cnblogs.com/Go7338395/p/13604465.html
Copyright © 2011-2022 走看看