题面在这里
题目大意:
给一个长度为n序列,n <= 300,问你有多少种排列方法,使得任意相邻两个数字的乘积都不是完全平方数。
做法:
很巧妙的DP+组合计数。
需要将数字分组。我们考虑将所有数字去掉平方因子后的数(相当于一个数开根号化成最简根式后根号里的数)相同的分到一组。
这个其实等价于,将所有数分解质因数后质因子的指数奇偶性相同的分到一组。
还等价于,两两之间相乘会变成平方数的分成一组。(额说到这了才是重点咳咳咳。。之前的帮助理解)
于是将问题转化为,同组的数字不能相邻的方案数。
然后我们按照这个组别进行dp。设f[i][j]表示前i组有j对同组相邻的数的方案数,记cnt[i]表示第i组数的个数。
这里我们假设同组的数都是无序的。(最后再乘上每组的全排列数即可)
转移的时候,考虑插入一组新的数,首先将这组的数分成k段,然后将其中的p段插入到之前j对同组相邻的数中,剩下的k-p段随意。
转移方程如下:
f[i][j+cnt[i]−k−p]+=f[i−1][j]∗C(cnt[i]−1,k−1)∗C(j,p)∗C(m−1−j+2,k−p);
需要好好理解qwq。
代码如下:
/*************************************************************
Problem: codeforces 841E - On the Bench
User: fengyuan
Language: C++
Result: Accepted
Time: 46 ms
Memory: 2800 KB
Submit_Time: 2017-12-06 11:02:30
*************************************************************/
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cctype>
#include<vector>
#include<map>
#include<queue>
#include<string>
#define rep(i, x, y) for (int i = (x); i <= (y); i ++)
#define down(i, x, y) for (int i = (x); i >= (y); i --)
#define mid ((l+r)/2)
#define lc (o<<1)
#define rc (o<<1|1)
#define pb push_back
#define mp make_pair
#define PII pair<int, int>
#define F first
#define S second
#define B begin()
#define E end()
using namespace std;
typedef long long LL;
//head
const int N = 305;
const int MOD = 1e9 + 7;
int n, tot, m;
int a[N], f[N][N], fac[N], C[N][N], cnt[N];
bool vis[N];
inline void prepare()
{
rep(i, 0, N-1) C[i][0] = 1;
rep(i, 1, N-1)
rep(j, 1, i) C[i][j] = (C[i-1][j] + C[i-1][j-1]) % MOD;
fac[0] = 1;
rep(i, 1, N-1) fac[i] = 1LL*fac[i-1]*i%MOD;
}
inline bool check(LL x)
{
return floor(sqrt(x)) == sqrt(x);
}
int main()
{
scanf("%d", &n);
prepare();
rep(i, 1, n) scanf("%d", &a[i]);
rep(i, 1, n){
if (vis[i]) continue; vis[i] = 1; cnt[++ tot] = 1;
rep(j, i+1, n)
if (check(1LL*a[i]*a[j])) vis[j] = 1, cnt[tot] ++;
}
f[1][cnt[1]-1] = 1; m = cnt[1];
rep(i, 2, tot){
rep(j, 0, m-1)
rep(k, 1, cnt[i])
rep(p, 0, k){
if (p > j) break;
f[i][j+cnt[i]-k-p] += 1LL*f[i-1][j]*C[cnt[i]-1][k-1]%MOD*C[j][p]%MOD*C[m-1-j+2][k-p]%MOD;
f[i][j+cnt[i]-k-p] %= MOD;
}
m += cnt[i];
}
int ans = f[tot][0];
rep(i, 1, tot) ans = 1LL*ans*fac[cnt[i]]%MOD;
printf("%d\n", ans);
return 0;
}