【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; }