Statement

给你一个长度为 nn 的序列 aia_i。问有多少 1n1 \sim n 的排列 pip_i,满足 i[2..n]\forall i \in [2.. n],有 api1×apia_{p_i − 1} \times a_{p_i} 不为完全平方数。答案对 109+710 ^ 9 + 7 取模。

数据范围:n300n \le 3001ai1091 \le a_i \le 10 ^ 9


Solution

每个数去掉平方因子后,条件等价于没有两个相等的数相邻。

Algorithm 1

把相同的数分类,假设共 mm 种不同的数,其中第 ii 种有 cic_i 个。记 f(i,j)f(i, j) 表示填入前 ii 种数,有 jj 对相同的数相邻的方案数;g(i,j)g(i, j) 表示将第 ii 种数拆分成块内块间均有序的 jj 块的方案数。下面设 si=j=1icjs_i = \sum_{j = 1} ^ i c_jt=j(cik)+xt = j - (c_i - k) + x

f(i,j)=k=1cix=0k(tx)(si1t+1kx)g(i,k)f(i1,t)g(i,j)=(ci1j1)ci!f(i, j) = \sum_{k = 1} ^ {c_i} \sum_{x = 0} ^ k \binom t x \cdot \binom {s_{i - 1} - t + 1} {k - x} \cdot g(i, k) \cdot f(i - 1, t)\\ g(i, j) = \binom {c_i - 1} {j - 1} \cdot c_i !

时间复杂度 O(n3)O(n ^ 3)

Algorithm 2

考虑容斥,设 f(i)f(i) 表示有至少 ii 对相等的数相邻的方案数,那么答案为 i=0n1(1)if(i)\sum_{i = 0} ^ {n - 1} (-1) ^ i \cdot f(i)

仍然相同的数分类,假设共 mm 种不同的数,其中第 ii 种有 cic_i 个。设 g(i,j)g(i, j) 表示在前 ii 种数中绑定 jj 对数的方案数。

g(i,j)=k=0ci1g(i1,jk)ci!(ci1k)(cik)!g(i, j) = \sum_{k = 0} ^ {c_i - 1} g(i - 1, j - k) \cdot \frac{c_i ! \cdot \binom{c_i - 1} k}{(c_i - k)!}

先假设有顺序,分子是从这 ci1c_i - 1 个空隙里选择 kk 个连起来;分母则是去掉顺序,留到下面在乘排列数。

所以答案为 i=0n1(1)i(ni)!g(m,i)\sum_{i = 0} ^ {n - 1} (-1) ^ i \cdot (n - i) ! \cdot g(m, i)

时间复杂度 O(n2)O(n ^ 2),可以使用分治 MTT 优化到 O(nlog2n)O(n \log ^ 2 n)


Code

Algorithm 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <bits/stdc++.h>
typedef long long LL; const int N = 307, V = 1e9, mod = 1e9 + 7;
LL qpow(LL a, int b) {
LL s = 1;
while(b) {
if(b & 1) s = s * a % mod;
a = a * a % mod, b >>= 1;
}
return s;
}
int n, m = 0, c[N], s[N]; LL fac[N], invf[N], f[N][N], g[N][N], ans = 0;
std::vector<int> vec; std::map<int, int> mp;
LL C(int n, int m) { return fac[n] * invf[m] % mod * invf[n - m] % mod; }
int main() {
scanf("%d", &n), fac[0] = 1;
for(int i = 1; i <= n; i++) fac[i] = fac[i - 1] * i % mod;
invf[n] = qpow(fac[n], mod - 2);
for(int i = n; i; i--) invf[i - 1] = invf[i] * i % mod;
for(int i = 2; i * i <= V; i++) vec.emplace_back(i * i);
for(int i = 1, x; i <= n; i++) {
scanf("%d", &x);
for(int y : vec) while(x % y == 0) x /= y;
mp[x]++;
}
for(auto p : mp) c[++m] = p.second, s[m] = s[m - 1] + c[m];
f[0][0] = 1;
for(int i = 1; i <= m; i++) for(int k = 1; k <= c[i]; k++) {
g[i][k] = C(c[i] - 1, k - 1) * fac[c[i]] % mod;
for(int j = 0; j < n; j++) for(int x = 0; x <= k; x++) {
int t = j - (c[i] - k) + x;
f[i][j] = (f[i][j] + C(t, x) * C(s[i - 1] - t + 1, k - x) % mod
* g[i][k] % mod * f[i - 1][t]) % mod;
}
}
return printf("%lld\n", f[m][0]), 0;
}

Algorithm 2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <bits/stdc++.h>
typedef long long LL; const int N = 307, V = 1e9, mod = 1e9 + 7;
LL qpow(LL a, int b) {
LL s = 1;
while(b) {
if(b & 1) s = s * a % mod;
a = a * a % mod, b >>= 1;
}
return s;
}
int n, m = 0, c[N]; LL fac[N], invf[N], g[N][N], ans = 0;
std::vector<int> vec; std::map<int, int> mp;
LL C(int n, int m) { return fac[n] * invf[m] % mod * invf[n - m] % mod; }
int main() {
scanf("%d", &n), fac[0] = 1;
for(int i = 1; i <= n; i++) fac[i] = fac[i - 1] * i % mod;
invf[n] = qpow(fac[n], mod - 2);
for(int i = n; i; i--) invf[i - 1] = invf[i] * i % mod;
for(int i = 2; i * i <= V; i++) vec.emplace_back(i * i);
for(int i = 1, x; i <= n; i++) {
scanf("%d", &x);
for(int y : vec) while(x % y == 0) x /= y;
mp[x]++;
}
for(auto p : mp) c[++m] = p.second;
g[0][0] = 1;
for(int i = 1; i <= m; i++) for(int j = 0; j < n; j++)
for(int k = 0; k < std::min(c[i], j + 1); k++)
g[i][j] = (g[i][j] + g[i - 1][j - k] * fac[c[i]] % mod *
C(c[i] - 1, k) % mod * invf[c[i] - k]) % mod;
for(int i = 0; i < n; i++)
ans = (ans + (i & 1 ? -1 : 1) * fac[n - i] * g[m][i]) % mod;
return printf("%lld\n", (ans + mod) % mod), 0;
}