题目大意是给你n个数,求相邻两数相乘不是完全平方数的排列数。
一开始看到这题的时候,本人便想给相乘为完全平方数的数对建边,然后就写萎了...
后来通过集体智慧发现这个重要性质:对于自然数a,b,c,若a*b为完全平方数,且b*c为完全平方数,那么a*c就是完全平方数。(我居然没想到)因此就可以对这n个数进行分组,使得每一组中两个数两两相乘为完全平方数,不同组的数两两相乘不是完全平方数。假设分成tot组,第i组的数的个数为num[i]。这也就等同于相同的数不能相邻的排列问题。
于是通过PY就得到了动规的做法:
定义数组dp[i,j],其中i表示前i组数,j表示有多少对相邻的同组的数(这也等价于有j个地方需要插入其他的数),而dp[i,j]就表示在当前状态下的方案数。那么最终答案就是dp[tot,0]。
定义m为前i-1组数的元素个数之和,在dp过程中维护。
当新加入第i组数的时候,把这num[i]个数分成k份(方案数是C(num[i]-1,k-1)),插入到m+1个空位中。
此时需要分类讨论:
在这m+1个空位中,有j个空位是特殊的,若插入其中会影响新生成的排列的相邻同组数的数目。于是设k份中的p份插入这j个空位中,剩下k-p份插入m+1-j个空位中。
就可以得到dp[i,j+num[i]-k-p]+=dp[i-1,j]*C(num[i-1],k-1)*C(j,p)*C(m+1-j,k-p);
这我一开始也不是很理解...
当然代码与这里有所差异。
注:因为dp是基于每一组数中的元素相同的前提的,所以最后答案还要乘以每一组数的A(num[i],num[i])。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=310,MOD=1000000007; 4 long long num[N],c[N][N]; 5 long long dp[N][N],a[N]; 6 int tmp,n,tot,example[N]; 7 bool key; 8 bool ask(int x,int y) 9 { 10 long long re=1; 11 re*=x; 12 re*=y; 13 long long f=sqrt(re); 14 if(f*f==re) return 1; 15 return 0; 16 } 17 int main() 18 { 19 cin>>n; 20 for(int i=1;i<=n;i++) 21 { 22 cin>>tmp; 23 key=1; 24 for(int j=1;j<=tot;j++) 25 { 26 if(ask(tmp,example[j])) 27 { 28 num[j]++; 29 key=0; 30 break; 31 } 32 } 33 if(key) 34 { 35 num[++tot]=1; 36 example[tot]=tmp; 37 } 38 } 39 for(int i=0;i<=n;i++) 40 c[i][0]=1; 41 for(int i=1;i<=n;i++) 42 for(int j=1;j<=i;j++) 43 { 44 c[i][j]=c[i-1][j]+c[i-1][j-1]; 45 c[i][j]%=MOD; 46 } 47 int m=0; 48 a[1]=1; 49 for(int i=2;i<=n;i++) 50 { 51 a[i]=a[i-1]*i; 52 a[i]%=MOD; 53 } 54 dp[0][0]=1; 55 long long temp,temp1; 56 for(int i=1;i<=tot;i++) 57 { 58 for(int j=0;j<n&&j<=m+1;j++) 59 { 60 if(dp[i-1][j]==0) continue; 61 for(int k=0;k<num[i];k++) 62 { 63 temp=dp[i-1][j]*c[num[i]-1][k];temp%=MOD; 64 for(int p=0;p<=k+1&&p<=j+num[i]-1-k;p++) 65 { 66 temp1=temp*c[j][p];temp1%=MOD;//本人因为数据溢出炸了几次 67 dp[i][j+num[i]-1-k-p]+=(temp1*c[m+1-j][k+1-p])%MOD; 68 dp[i][j+num[i]-1-k-p]%=MOD; 69 } 70 } 71 } 72 m+=num[i]; 73 } 74 for(int i=1;i<=tot;i++) 75 { 76 dp[tot][0]*=a[num[i]]; 77 dp[tot][0]%=MOD; 78 } 79 cout<<dp[tot][0]<<endl; 80 return 0; 81 }