2016北京集训测试赛(十七)Problem A: crash的游戏

Description

Solution

相当于要你计算这样一个式子:

[sum_{x = 0}^m left( egin{array}{} m \ x end{array} ight) left( egin{array}{} k \ n - m + 2x end{array}{} ight) ]

考虑到(m)非常大, 而(k)却比较小, 我们尝试将(x)(m)相关转化为(k)相关. 我们用如下现实意义来考虑: 令(N = n - m), 则我们相当于有两堆球, 第一堆球以单个球为单位, 总共(N)个, 我们要在其中取出(i)个; 第二堆球以一对球连在一起为单位, 总共有(m)对, 我们要在其中取出任意多对球, 拆开放入第一堆中, 然后问你在第一堆中取(k)个球总共有多少种的方案.
这个问题仍然不好解决, 我们继续考虑: 我们假设这(k)个球中, 总共用了第二堆球中的(j)对. 也就是说, 这(k)个球中(i)个是在第一堆中取来的, 剩下(k - i)个用到了第二堆中的(j)对球, 其中每一对既有可能被用了一个, 也有可能用了两个. 考虑方案数: 在第一堆中取(i)个显然就是(left( egin{array}{} N \ i end{array}{} ight)); 我们用f[i][j]来表示在第2堆中用到(i)对, 并且总共取出(j)个球的方案数, 考虑(f[i][j])的递归式:

[f[i][j] = f[i - 1][j - 1] imes 2 + f[i - 1][j - 2] ]

这样以来, 总共的方案数就是

[sum_i sum_j left( egin{array}{} N \ i end{array}{} ight) f[i][N - k] ]

考虑到询问有(500)组, 我们要作一些预处理, 记忆化某些信息.

 
#include <cstdio>
#include <cctype>
 
namespace Zeonfai
{
    inline int getInt()
    {
        int a = 0, sgn = 1; char c;
        while(! isdigit(c = getchar())) if(c == '-') sgn *= -1;
        while(isdigit(c)) a = a * 10 + c - '0', c = getchar();
        return a * sgn;
    }
}
const int K = 300, MOD = (int)1e9 + 7;
int prod[K + 1], prodInv[K + 1], f[K + 1][K << 2], dwn[K + 1], _dwn[K + 1], pw[K + 1];
inline int power(int a, int x)
{
    if(x < 0) return 0;
    int res = 1;
    for(; x; a = (long long)a * a % MOD, x >>= 1) if(x & 1) res = (long long)res * a % MOD;
    return res;
}
inline int _C(int m)
{
    if(m < 0) return 0;
    return (long long)_dwn[m] * prodInv[m] % MOD;
}
inline int C(int m)
{
    if(m < 0) return 0;
    return (long long)dwn[m] * prodInv[m] % MOD;
}
inline void pretreat()
{
    prod[0] = prodInv[0] = 1; for(int i = 1; i <= K; ++ i) prod[i] = (long long)prod[i - 1] * i % MOD, prodInv[i] = power(prod[i], MOD - 2);
    f[0][0] = 1;
    for(int i = 1; i <= K; ++ i) for(int j = 1; j <= i << 1; ++ j) f[i][j] = (f[i - 1][j - 1] * 2 % MOD + (j >= 2 ? f[i - 1][j - 2] : 0)) % MOD;
}
int main()
{
 
    #ifndef ONLINE_JUDGE
 
    freopen("crash.in", "r", stdin);
    freopen("crash.out", "w", stdout);
 
    #endif
 
    using namespace Zeonfai;
    pretreat();
    for(int T = getInt(); T --; )
    {
        int n = getInt(), m = getInt(), k = getInt(), ans = 0;
        dwn[0] = 1; dwn[1] = m; for(int i = 2; i <= k; ++ i) dwn[i] = (long long)dwn[i - 1] * (m - i + 1) % MOD;
        _dwn[0] = 1; _dwn[1] = n - m; for(int i = 2; i <= k; ++ i) _dwn[i] = (long long)_dwn[i - 1] * (n - m - i + 1) % MOD;
        for(int i = 0; i <= k; ++ i) pw[i] = power(2, m - i);
        for(int i = 0; i <= k; ++ i)
        {
            int cur = 0;
            for(int j = (k - i + 1) / 2; j <= k - i; ++ j)
                cur = (cur + (long long)C(j) % MOD * pw[j] % MOD * f[j][k - i] % MOD) % MOD;
            ans = (ans + (long long)cur * _C(i) % MOD) % MOD;
        }
        printf("%d
", ans);
    }
}
原文地址:https://www.cnblogs.com/ZeonfaiHo/p/7464873.html