<h2>题目大意</h2>
给你一个长度为 $n$ 的序列,问你有多少种置换,使得置换后的序列相邻两项的乘积都不是平方数。
<h2>题解</h2>
这个题目好像比较厉害的样子。。首先把这些数按照是否能组成平方数分组,同一组里都是两两相乘能凑成平方数的,可以证明存在这样的分配方案。
然后我们可以设 $f_{i,j}$ 表示插入了前 $i$ 个组,有 $j$ 个不满足的相邻关系的方案数。
我们令 $tot$ 表示已经填完的个数,$sz$表示这一组的大小,$d$ 表示该次插入隔开了原来多少个组,$k$ 表示将这些数分成了多少组。我们不考虑顺序,能得到转移:
$$f_{i-1,j}* T \to f_{i,j-d+sz-k}$$
其中
$$T = \binom {sz-1} {k-1} \binom j d \binom {tot-j+1} {k-d}$$
然后最后答案乘上每组的阶乘,就好了。
/* * Author: RainAir * Time: 2019-08-28 08:59:34 */ #include <algorithm> #include <iostream> #include <cstring> #include <climits> #include <cstdlib> #include <cstdio> #include <bitset> #include <vector> #include <cmath> #include <ctime> #include <queue> #include <stack> #include <map> #include <set> #define fi first #define se second #define U unsigned #define P std::pair #define Re register #define LL long long #define pb push_back #define MP std::make_pair #define all(x) x.begin(),x.end() #define CLR(i,a) memset(i,a,sizeof(i)) #define FOR(i,a,b) for(Re int i = a;i <= b;++i) #define ROF(i,a,b) for(Re int i = a;i >= b;--i) #define DEBUG(x) std::cerr << #x << '=' << x << std::endl #define int LL const int MAXN = 300+5; const int ha = 1e9 + 7; int fac[MAXN],inv[MAXN]; inline int qpow(int a,int n=ha-2){ int res = 1; while(n){ if(n & 1) res = 1llresa%ha; a = 1llaa%ha; n >>= 1; } return res; } inline void prepare(){ fac[0] = 1; FOR(i,1,MAXN-1) fac[i] = 1llfac[i-1]i%ha; inv[MAXN-1] = qpow(fac[MAXN-1]); ROF(i,MAXN-2,0) inv[i] = 1llinv[i+1](i+1)%ha; } inline int C(int n,int m){ if(n < m) return 0; if(!m) return 1; return 1llfac[n]inv[m]%ha*inv[n-m]%ha; } int n,a[MAXN]; bool vis[MAXN];int sz[MAXN],cnt; int fMAXN; inline int pd(int x){ int q = std::sqrt(1.0*x); return q*q == x; } signed main(){ scanf("%lld",&n);prepare(); FOR(i,1,n) scanf("%lld",a+i); FOR(i,1,n){ if(vis[i]) continue; sz[++cnt] = 1;vis[i] = true; FOR(j,1,n){ if(vis[j]) continue; if(pd(a[i]*a[j])){ vis[j] = true;sz[cnt]++; } } } f0 = 1;int tot = 0; FOR(i,1,cnt){ FOR(j,0,tot){ FOR(k,1,std::min(sz[i],tot+1)){ FOR(d,0,std::min(j,k)){ int T = C(j,d)C(sz[i]-1,k-1)%haC(tot-j+1,k-d)%ha; (fi-k] += fi-1*T%ha)%=ha; } } } tot += sz[i]; } int ans = fcnt; FOR(i,1,cnt) ans = 1llansfac[sz[i]]%ha; printf("%lldn",ans); return 0; }