codeforces841E
来源:互联网 发布:赚钱红包软件秒到账 编辑:程序博客网 时间:2024/06/06 10:44
题面在这里
题目大意:
给一个长度为n序列,n <= 300,问你有多少种排列方法,使得任意相邻两个数字的乘积都不是完全平方数。
做法:
很巧妙的DP+组合计数。
需要将数字分组。我们考虑将所有数字去掉平方因子后的数(相当于一个数开根号化成最简根式后根号里的数)相同的分到一组。
这个其实等价于,将所有数分解质因数后质因子的指数奇偶性相同的分到一组。
还等价于,两两之间相乘会变成平方数的分成一组。(额说到这了才是重点咳咳咳。。之前的帮助理解)
于是将问题转化为,同组的数字不能相邻的方案数。
然后我们按照这个组别进行dp。设f[i][j]表示前i组有j对同组相邻的数的方案数,记cnt[i]表示第i组数的个数。
这里我们假设同组的数都是无序的。(最后再乘上每组的全排列数即可)
转移的时候,考虑插入一组新的数,首先将这组的数分成k段,然后将其中的p段插入到之前j对同组相邻的数中,剩下的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;//headconst 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;}