题目链接:
题目:
题目大意:
有 T ( 1 ≤ T ≤ 300 ) T(1\leq T \leq 300) T(1≤T≤300)组测试,每组给定 n ( 2 ≤ n ≤ 3000 ) n (2\leq n \leq 3000) n(2≤n≤3000),询问满足 ∑ i = 1 n a i = ∏ i = 1 n a i \sum_{i=1}^{n}a_i=\prod_{i=1}^{n}a_i ∑i=1nai=∏i=1nai的长度为n的序列有多少种。
解题思路:
因为乘积的增长速度是特别快的,所以如果我们把序列
a
a
a从大到小排序,那么,显然,序列的最后面会有相当多的1,当后面全部是1的时候,我们就不用搜索了,这样可以节省相当大的一部分时间。易证序列中最大的数不会超过
n
n
n。
暴力dfs+剪枝即可,具体见代码。
代码:
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 3000 + 100, INF = 0x3f3f3f3f;
const ll MOD = 1000000007;
ll fac[MAXN], facInv[MAXN];
int temp[MAXN];
ll getInv(ll val) {
ll b = MOD - 2, res = 1;
while (b) {
if (b & 1) res = res * val % MOD;
val = val * val % MOD;
b >>= 1;
}
return res;
}
void init() {
fac[0] = 1;
for (int i = 1; i < MAXN; i++) fac[i] = fac[i - 1] * i % MOD;
facInv[MAXN - 1] = getInv(fac[MAXN - 1]);
for (int i = MAXN - 2; i >= 0; i--) facInv[i] = facInv[i + 1] * (i + 1) % MOD;
}
ll dfs(int n, int cur = 1, int pre = INF, int sum = 0, int mul = 1) {
if (sum + n - cur + 1 == mul) {//从当前位置开始所有的数全为1
ll ans = fac[n], num = 1;
for (int i = 2; i <= cur; i++) {
if (temp[i] == temp[i - 1]) num++;
else ans = ans * facInv[num] % MOD, num = 1;
}
ans = (ans * facInv[num] % MOD) * facInv[n - cur + 1] % MOD;
return ans;
}
if (cur > n || pre == 1) return 0;
ll ans = 0;
for (int i = min(n, pre); i >= 1; i--)
if (sum + n - cur + i >= mul * i)
temp[cur] = i, ans = (ans + dfs(n, cur + 1, i, sum + i, mul * i)) % MOD;
return ans;
}
int main() {
int T, n;
scanf("%d", &T);
init();
while (T--) {
scanf("%d", &n);
printf("%lld\n", dfs(n));
}
return 0;
}