【CF840C】On the Bench
题意:给你一个长度为n的数组{ai},定义一个1到n的排列是合法的,当且仅当对于$1\le i <n$,$a_i\times a_{i+1}$不是完全平方数。求所有合法的排列个数。
$n\le 300,a_i\le 10^9$
题解:显然我们先把ai中的平方因子除掉,然后就变成了任意相邻两数不能相同的排列数。显然要将相同的数放到一起处理。
考虑DP,令f[i][j][k]表示枚举到第i个数,一共有j个相邻的位置是相同的,在之前所有和ai相同的数中,有k个相邻的位置 的方案数。转移复杂度$O(n^3)$。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll P=1000000007;
int n;
int v[310];
ll f[2][310][310];
inline void upd(ll &x,const ll &y) {x+=y; if(x>=P) x%=P;}
int main()
{
scanf("%d",&n);
int i,j,k,d=0,tmp=0,t;
for(i=1;i<=n;i++)
{
scanf("%d",&t),v[i]=1;
for(j=2;j*j<=t;j++) if(t%j==0)
{
tmp=0;
while(t%j==0) tmp^=1,t/=j;
if(tmp) v[i]*=j;
}
if(t>1) v[i]*=t;
}
sort(v+1,v+n+1);
f[0][0][0]=1;
for(i=1;i<=n;i++)
{
if(v[i]>v[i-1])
{
for(j=0;j<=i;j++) for(k=1;k<=tmp;k++) upd(f[d][j][0],f[d][j][k]),f[d][j][k]=0;
tmp=0;
}
d^=1,memset(f[d],0,sizeof(f[d]));
for(j=0;j<=i;j++) for(k=0;k<=tmp&&k<=j;k++)
{
upd(f[d][j+1][k+1],f[d^1][j][k]*(2*tmp-k));
if(j) upd(f[d][j-1][k],f[d^1][j][k]*(j-k));
upd(f[d][j][k],f[d^1][j][k]*(i-(2*tmp-k)-(j-k)));
}
tmp++;
}
printf("%lld",f[d][0][0]);
return 0;
}