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);
}
}