前置技能:多重集的排列数
设 是由 组成的多重集。S的全排列个数位
思路
假设我们构造的序列的乘积为f,因为 ,所以可以肯定这个对于构造的序列来说非1的数不会太多,最多也就是10多个,此外f是小于等于n*2的,具体证明不是很清楚,但是打表出来的结果确实是这样,于是猜了一下过了。所以说只要我们能够找出非1的数的大小及数量,那么就可以用多重集的排列数计算了。
由于它的个数不会太多,并且也不大(小于2*n),所以可以用搜索取逐一搜索出来。
搜索是一门艺术
AC_CODE
时间比预期的还优秀,200ms+就A了。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod = 1e9 + 7;
ll n;
ll a[3000], cnt, f[1000];
ll ans;
ll fspow(ll x, ll n) {
ll ans = 1;
while (n) {
if (n & 1)ans = ans * x%mod;
n >>= 1;
x = x * x%mod;
}
return ans;
}
void init() {
f[0] = 1;
for (int i = 1; i < 1000; i++)f[i] = f[i - 1] * i%mod;
}
void solve(int k) {
ll res = 1;
for (int i = n - k + 1; i <= n; i++)res = res * i%mod;
int c = 0;
a[0] = a[1]; a[cnt + 1] = a[cnt] + 1;
for (int i = 1; i <= cnt + 1; i++) {
if (a[i] == a[i - 1])c++;
else {
res = res * fspow(f[c], mod - 2) % mod;
c = 1;
}
}
ans = (ans + res) % mod;
}
void dfs(int st, int k, int f, int sum) {
if (f > 2 * n)return;
if (sum + n - k > 2 * n)return;
if (f == sum + n - k) {
solve(k);
return;
}
for (int i = st; i <= n && f*i <= 2 * n; i++) {
a[++cnt] = i;
dfs(i, k + 1, f*i, sum + i);
cnt--;
}
}
int main() {
init();
int t; scanf("%d", &t);
while (t--) {
cnt = 0;
scanf("%lld", &n);
ans = 0;
dfs(2, 0, 1, 0);
printf("%lld\n", ans);
}
return 0;
}