题目概述
题目链接:https://www.luogu.com.cn/problem/CF840C。
给你 \(n\) 给数,将他们排列成一个序列并满足相邻两项 \(a_i,a_{i+1}\) 相乘不为平方数。问方案。
分析
我只说明一种解法,其他类的解法总结见:https://www.luogu.com.cn/article/nf3ftw8t。
注意到相邻两项的乘积不是平方数,要么是神秘题目,要么就是性质。
考虑怎么判断两个数 \(a,b\) 相乘是平方数,我们肯定先把他们的平方因子去掉,然后就变成了 \(a=n^2x,b=m^2y\),那么当 \(ab\) 为平方数时,当且仅当 \(x=y\)。
也就是说这道题目转化为了一个相邻两个数不相等的方案。
所以感觉很经典就记录了一下。
像这样的题目(类似求排列方案的)一般考虑用桶分类然后一类一类地插入到序列当中,所以就是有一种计数dp叫做插入dp,其中有一个小类是连续子段dp。
那么先对这个数进行离散化然后记录,枚举每种数,并记数量为 \(cnt_i\)。
不难想到设 \(f_{i,j}\) 表示考虑了前 \(i\) 种数,现在有 \(j\) 个相邻的不合法的位置的方案。
显然是由 \(f_{i-1,j}\rightarrow f_{i,?}\)。
现在考虑怎么将第 \(i\) 种数插入进去呢?那么我们可以类似地想到分成 \(k\) 个部分然后分别插入到某些位置。
那么问题在于这类的方案是什么呢?记为 \(g_{i,j}\) 表示 \(i\) 个数分成 \(j\) 个部分的方案(类似于斯特林数)。
那么转移是比较显然的:
为什么是 \((i-1+j)\) 呢,这就是我当前这个数可以插入到任意一个块,也就是说插在头有 \(j\) 种,插在中间和末尾有 \(i-1\) 种。
然后我们再考虑怎么转移 \(f\)。
对于 \(f_{i-1,j}\) 对谁有贡献呢?
我们枚举 \(k\) 表示分成 \(k\) 个部分,枚举 \(l\) 表示要从这 \(j\) 个不合法的位置选出 \(l\) 个将 \(k\) 个中的一些插入进去,最后剩下的块就在剩下的位置里面选。
转移即为:
其中 \(sum\) 前面位置的个数。
为什么不对选出来的方案来个阶乘呢?这是因为我们求 \(g\) 的时候已经考虑到了顺序。
代码
时间复杂度 \(\mathcal{O}(n^2\sum cnt_i)=\mathcal{O}(n^3)\)。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <stdlib.h>
#include <algorithm>
#include <vector>
#define int long long
#define N 305
#define getid(x) (lower_bound(ls.begin(),ls.end(),x) - ls.begin() + 1)
using namespace std;
const int mod = 1e9 + 7;
int jc[N],inv[N];
int C(int a,int b) {if (a < 0 || b < 0 || a < b) return 0;return jc[a] * inv[b] % mod * inv[a - b] % mod;
}
int n,a[N],cnt[N],g[N][N],f[N][N];
void pls(int &x,int y) {(x += y) %= mod;
}
signed main(){jc[0] = jc[1] = inv[0] = inv[1] = 1;for (int i = 2;i < N;i ++) jc[i] = jc[i - 1] * i % mod,inv[i] = (mod - mod / i) * inv[mod % i] % mod;for (int i = 2;i < N;i ++) inv[i] = inv[i - 1] * inv[i] % mod;cin >> n;vector<int> ls;for (int i = 1;i <= n;i ++) scanf("%lld",&a[i]);for (int i = 1;i <= n;i ++) {int x = a[i];a[i] = 1;for (int j = 2;j * j <= x;j ++)if (x % j == 0) {int cntx = 0;while(x % j == 0) cntx ++,x /= j;if (cntx & 1) a[i] *= j;}a[i] *= x;ls.push_back(a[i]);}stable_sort(ls.begin(),ls.end());ls.erase(unique(ls.begin(),ls.end()),ls.end());for (int i = 1;i <= n;i ++) a[i] = getid(a[i]),cnt[a[i]] ++;int m = ls.size();g[0][0] = 1;for (int i = 1;i <= n;i ++)for (int j = 1;j <= i;j ++)g[i][j] = (g[i - 1][j - 1] * j % mod + g[i - 1][j] * (i + j - 1) % mod) % mod;int sum = 0;f[0][0] = 1;for (int i = 1;i <= m;i ++) {for (int j = 0;j <= sum;j ++)for (int k = 1;k <= cnt[i];k ++)for (int l = 0;l <= j;l ++)pls(f[i][j - l + cnt[i] - k],f[i - 1][j] * g[cnt[i]][k] % mod * C(j,l) % mod * C(sum + 1 - j,k - l) % mod);sum += cnt[i];// for (int j = 0;j <= sum;j ++) cout << f[i][j] << ' ';// cout << '\n';}cout << f[m][0];return 0;
}