Description
给定
n
个数,求
Solution
首先先把每个数的平方因子全部去掉,就形成了
vn
个等价类,每个大小为
vi
,那么只要计算出这些等价类内不考虑顺序的答案
ans
,最后答案就是
ans∏vni=1vi!
。
考虑DP。
令
dpi,p
为前
p
个等价类,存在
- 枚举从已选的非法的
pairs
的个数
i
。(总共
usd 个) - 枚举将待选的数分成
j
块。(总共
c 个) - 枚举从
j
块中取
k 块。
把这
k
块插入到非法的
dpi,p(c−1j−1)(ik)(usd−i+1j−k)
-
(c−1j−1)
是
c
个数形成
c−1 个 pairs , j 个数形成j−1 个 pairs 。 -
(ik)
是在
i
个中选
k 的贡献。 -
(usd−i+1j−k)
是把那
k
块数插到
i 个数中间之后,有 j−k 个数可以随便插在 usd−i+1 个位置中。
为了保证上述枚举的可行性(即把数插入的时候有足够的位置可以插入),将等价类按大小降序排序后直接DP就好了。
然后这个是可以滚动数组存一存的。。。
有一些小细节,可以看代码QAQ。
#include <map>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
inline char get(void) {
static char buf[100000], *S = buf, *T = buf;
if (S == T) {
T = (S = buf) + fread(buf, 1, 100000, stdin);
if (S == T) return EOF;
}
return *S++;
}
inline void read(int &x) {
static char c; x = 0; int sgn = 0;
for (c = get(); c < '0' || c > '9'; c = get()) if (c == '-') sgn = 1;
for (; c >= '0' && c <= '9'; c = get()) x = x * 10 + c - '0';
if (sgn) x = -x;
}
typedef long long ll;
const int N = 303;
const int MOD = 1000000007;
int C[N][N];
int dp[N], dp1[N];
int n, vn, usd, c, ans;
int a[N], mp[N], v[N];
map<int, int> buc;
inline int Mod(int x) {
while (x >= MOD) x-= MOD;
return x;
}
inline void Add(int &x, int y) {
x += y; x = Mod(x);
}
int main(void) {
freopen("1.in", "r", stdin);
read(n);
C[0][0] = 1;
for (int i = 1; i <= n; i++)
for (int j = 0; j < n; j++)
C[i][j] = (j == 0 ? 1 : Mod(C[i - 1][j - 1] + C[i - 1][j]));
for (int i = 0; i < n; i++) {
read(a[i]);
for (int j = 2; j * j <= a[i]; j++)
while (a[i] % (j * j) == 0)
a[i] /= j * j;
if (!buc[a[i]]) mp[vn++] = a[i];
++buc[a[i]];
}
for (int i = 0; i < vn; i++) v[i] = buc[mp[i]];
sort(v, v + vn);
reverse(v, v + vn);
usd = v[0];
dp[usd - 1] = 1;
for (int it = 1; it < vn; it++) {
c = v[it];
for (int i = usd + c; i >= 0; i--) dp1[i] = 0;
for (int i = 0; i < usd; i++) {
if (dp[i] == 0) continue;
for (int j = 1; j <= c; j++)
for (int k = 0; k <= i && k <= j; k++)
if (usd - i + 1 >= j - k)
Add(dp1[i - k + c - j], (ll)dp[i] * C[c - 1][j - 1] % MOD * C[i][k] % MOD * C[usd - i + 1][j - k] % MOD);
}
usd += c;
for (int i = 0; i <= usd; i++) dp[i] = dp1[i];
}
ans = dp[0];
for (int i = 0; i < vn; i++)
for (int j = 1; j <= v[i]; j++)
ans = (ll)ans * j % MOD;
printf("%d\n", ans);
return 0;
}