CF 932E Team Work

推式子题。

首先有公式:

$$n^k = \sum_{i = 0}^{k}\binom{n}{i}S(k, i)*i!$$

其中$S$表示第二类斯特林数。

左边表示$k$个不同的小球放$n$个不同的盒子允许有空盒的方案数,而右边先枚举非空盒的数量,选择非空的盒子,然后再把$k$个小球全部放入,因为盒子是不同的,所以还要乘上$i!$。

把公式代进去:

$$\sum_{i = 1}^{n}\binom{n}{i}i^k$$

$$=\sum_{i = 1}^{n}\binom{n}{i}\sum_{j = 0}^{k}j!\binom{i}{j}S(k, j)$$

$$=\sum_{j = 0}^{k}j!S(k, j)\sum_{i = 1}^{n}\binom{n}{i}\binom{i}{j}$$

$$=\sum_{j = 0}^{k}j!S(k, j)\binom{n}{j}\sum_{i = 1}^{n}\binom{n - j}{n - i}$$

$$=\sum_{j = 0}^{k}S(k, j)\frac{n!}{(n - j)!}\sum_{i = 0}^{n - 1}\binom{n - j}{i}$$

注意到

$$\sum_{i = 0}^{n - 1}\binom{n - j}{i} = 2^{n - j} - [j == 0]$$

那么就可以直接算了。

第二类斯特林数有递推公式$S(n, m) = S(n - 1, m - 1) + m * S(n - 1, m)$。

时间复杂度$O(k^2)$。

Code:

#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;

const int N = 5005;
const ll P = 1e9 + 7;

ll f[N], s2[N][N], bin[N];

inline ll fpow(ll x, ll y) {
    ll res = 1;
    for (; y > 0; y >>= 1) {
        if (y & 1) res = res * x % P;
        x = x * x % P;
    }
    return res;
}

int main() {
    int n, k;
    scanf("%d%d", &n, &k);
    
    s2[0][0] = 1;
    for (int i = 1; i <= k; i++)
        for (int j = 1; j <= i; j++)
            s2[i][j] = (s2[i - 1][j - 1] + 1LL * j * s2[i - 1][j]) % P;
    
    f[0] = 1, bin[0] = fpow(2, n);
    ll inv2 = fpow(2, P - 2);
    for (int i = 1; i <= k; i++) {
        f[i] = f[i - 1] * (n - i + 1) % P;
        bin[i] = bin[i - 1] * inv2 % P;    
    }
    
    ll ans = 0;
    for (int i = 0; i <= k; i++)
        ans = (ans + s2[k][i] * f[i] % P * (bin[i] - (i == 0)) % P) % P;
    
    printf("%lld\n", ans);
    return 0;
}
View Code

 

posted @ 2019-01-01 14:45  CzxingcHen  阅读(202)  评论(0)    收藏  举报