codeforces841E

来源:互联网 发布:赚钱红包软件秒到账 编辑:程序博客网 时间:2024/06/06 10:44

题面在这里
题目大意:
给一个长度为n序列,n <= 300,问你有多少种排列方法,使得任意相邻两个数字的乘积都不是完全平方数。

做法:
很巧妙的DP+组合计数。
需要将数字分组。我们考虑将所有数字去掉平方因子后的数(相当于一个数开根号化成最简根式后根号里的数)相同的分到一组。
这个其实等价于,将所有数分解质因数后质因子的指数奇偶性相同的分到一组。
还等价于,两两之间相乘会变成平方数的分成一组。(额说到这了才是重点咳咳咳。。之前的帮助理解)
于是将问题转化为,同组的数字不能相邻的方案数。

然后我们按照这个组别进行dp。设f[i][j]表示前i组有j对同组相邻的数的方案数,记cnt[i]表示第i组数的个数。
这里我们假设同组的数都是无序的。(最后再乘上每组的全排列数即可)
转移的时候,考虑插入一组新的数,首先将这组的数分成k段,然后将其中的p段插入到之前j对同组相邻的数中,剩下的k-p段随意。
转移方程如下:
f[i][j+cnt[i]kp]+=f[i1][j]C(cnt[i]1,k1)C(j,p)C(m1j+2,kp);

需要好好理解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;}