CF840C 题解
CF840C On the Bench 题解
前言
提供一个
预处理
注意到
故相乘是平方数是可以传递的,于是我们如果把不能相邻的
转换
注意到互不相邻这个条件并不好转换,但是如果我们钦定两个数相邻,那么我们就可以把这两个数看作一个数进行后续操作,于是我们考虑子集反演
令全集
记
转移
对于一种颜色,考虑有
即
剩下的跑一个
统计答案
注意到在背包的过程中,我们并没有考虑不同段之间的位置关系,而有
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll M = 310, P = 1e9 + 7;
ll n, a[M], c[M], s[M], fac[M], inv[M], tc = 1, f[M][M], ans = 0;
ll qpow(ll a, ll b, ll res = 1)
{
for (; b; b >>= 1, a = a * a % P)
if (b & 1) res = res * a % P;
return res;
}
int main()
{
scanf("%lld", &n), fac[0] = 1;
for (int i = 1; i <= n; i++) scanf("%lld", a + i), fac[i] = fac[i - 1] * i % P;
inv[n] = qpow(fac[n], P - 2), f[0][0] = 1;
for (int i = n - 1; i >= 0; i--) inv[i] = inv[i + 1] * (i + 1) % P;
for (int i = 1; i <= n; i++)
if (!c[i])
{
c[i] = tc++;
for (int j = 1; j <= n; j++)
if ((long long) sqrt(a[i] * a[j]) * (long long) sqrt(a[i] * a[j]) == a[i] * a[j]) c[j] = c[i];
}
for (int i = 1; i <= n; i++) s[c[i]]++;
for (int i = 1; i < tc; i++)
for (int j = 0; j < s[i]; j++)
{
for (int k = j; k <= n; k++)
f[i][k] =
(f[i][k] + f[i - 1][k - j] * fac[s[i]] % P * fac[s[i] - 1] % P * inv[j] % P * inv[s[i] - j] % P * inv[s[i] - j - 1] % P) % P;
}
for (int i = 0; i <= n; i++) ans = (ans + (i & 1 ? -1 : 1) * f[tc - 1][i] * fac[n - i] % P) % P;
if (ans < 0) ans += P;
printf("%lld\n", ans);
return 0;
}