Codeforces Round #429 (Div. 2) E. On the Bench
A year ago on the bench in public park Leha found an array of n numbers. Leha believes that permutation p is right if for all 1 ≤ i < n condition, that api·api + 1 is not perfect square, holds. Leha wants to find number of right permutations modulo 109 + 7.
First line of input data contains single integer n (1 ≤ n ≤ 300) — length of the array.
Next line contains n integers a1, a2, ... , an (1 ≤ ai ≤ 109) — found array.
Output single integer — number of right permutations modulo 109 + 7.
3
1 2 4
2
7
5 2 4 2 4 1 1
144
For first example:
[1, 2, 4] — right permutation, because 2 and 8 are not perfect squares.
[1, 4, 2] — wrong permutation, because 4 is square of 2.
[2, 1, 4] — wrong permutation, because 4 is square of 2.
[2, 4, 1] — wrong permutation, because 4 is square of 2.
[4, 1, 2] — wrong permutation, because 4 is square of 2.
[4, 2, 1] — right permutation, because 8 and 2 are not perfect squares.
题意 :求相邻的元素相乘不为平方数的方案数(a[0]=a[1]=1, 视 a[0] 与 a[1] 不同)
思路 :
每个数可以表示为 p1^a1 * p2^a2 * .....
如果 两个数A,B相乘为平方数 则 a1%2 = a1' %2 , a2%2 = a2'%2 .....
即 对应质因子的幂次 奇偶性相同 这样就可以划分出T组
然后题目就转化为 T种物品 相同种类物品不能放在相邻 求方案数
这题就变成原题 :https://csacademy.com/contest/archive/task/distinct_neighbours/statement/
http://acm.hdu.edu.cn/showproblem.php?pid=6116
做法为dp
dp [ i ] [ j ] 表示 插入第 i 组的物品 出现了 左右为相同物品的空隙个数为 j 的方案数
那 dp [ T ] [ 0 ] 就是最终答案了
附: cs官方题解 (原题的题解)
First we group all the distinct values in the array. Then we can solve the problem using dynamic programming:
Let dp[i][j] = the number of distinct arrays that can be obtained using the elements of the first i groups such that there are exactly j pairs of consecutive positions having the same value. The answer can be found in dp[distinctValues][0].
Now let's say the sum of frequences of the first i values is X. This means the arrrays we can build using the elements from these i groups have size X, so we can insert the elments of group i + 1 in X + 1 gaps: before the first element, after the last, and between any two consecutive. We can fix the number k of gaps where we want to insert at least one element from group i + 1, but we also need to fix the number l of these k gaps which will be between elements that previously had the same value. State dp[i][j] will update the state dp[i + 1][j - l + frequence[i + 1] - k].
The number of ways of choosing k gaps such that exactly l are between consecutive elements having the same value can be computed easily using combination formulas. We are left with finding out the number of ways of inserting frequence[i + 1] elements in k gaps. This is also a classical problem with a simple answer: Comb(frequence[i + 1] - 1, k - 1).
具体转移方程见代码 :
#include <stdio.h> #include <string.h> #include <ctype.h> #include <stdlib.h> #include <assert.h> #include <math.h> #include <set> #include <map> #include <stack> #include <queue> #include <vector> #include <string> #include <iostream> #include <algorithm> #include <functional> #define mp make_pair #define pb push_back #define mes(a,b) memset(a,b,sizeof(a)) #define mes0(a) memset(a,0,sizeof(a)) #define lson l,mid,pos<<1 #define rson mid+1,r,pos<<1|1 #define rep(i,a,b) for(int i=a;i<=b;i++) #define fi first #define se second #define sss(a) a::iterator #define all(a) a.begin(),a.end() using namespace std; typedef double DB; typedef long long LL; typedef pair<int,int> pii; typedef pair<long long ,int> pli; typedef pair<int,long long > pil; typedef pair<string,int> psi; typedef pair<long long ,long long > pll; const int inf = 0x3f3f3f3f; const long long INF = 0x3f3f3f3f3f3f3f3f; const double pi = acos(-1.0); const int maxn = 100000+10; const int mod = 1e9+7; LL dp[500][500]; LL C[500][500]; LL fact[500]; int cnt[500]; int a[500]; int vis[500]; int sz; int check(LL x) { LL l=1,r=1e9; LL now=l; while (l<=r){ LL mid=(l+r)>>1; if (mid*mid<=x)now=mid,l=mid+1; else r=mid-1; } return now*now==x; } inline LL M(LL x) { return x%mod; } void init() { C[0][0]=1; fact[0]=1; for (int i=1;i<=300;i++){ C[i][0]=1; for (int j=1;j<=i;j++){ C[i][j]=M(C[i-1][j]+C[i-1][j-1]); } } for (int i=1;i<=300;i++)fact[i]=M(fact[i-1]*i); } void slove() { dp[0][cnt[0]-1]=1; int lim=cnt[0]; for (int i=1;i<sz;i++){ for (int j=0;j<lim;j++){ /// dp[i-1][j] for (int k=0;k<cnt[i];k++){/// group[i]分成k+1组 ,cnt[i]-1-k个空隙 for (int m=0;m<=min(j,k+1);m++){ /// 选了m个左右相同的空隙插入 dp[i][j+cnt[i]-1-k-m]=M(dp[i][j+cnt[i]-1-k-m]+dp[i-1][j]*C[cnt[i]-1][k]%mod*C[j][m]%mod*C[lim-1-j+2][k+1-m]); } } } lim+=cnt[i]; } LL ans=dp[sz-1][0]; for (int i=0;i<sz;i++){ ans*=fact[cnt[i]]; ans%=mod; } cout<<ans<<endl; } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int n; scanf("%d",&n); for (int i=0;i<n;i++){ scanf("%d",a+i); } for (int i=0;i<n;i++){ if (vis[i]==0){ for (int j=i;j<n;j++){ if (check(1LL*a[i]*a[j])){ cnt[sz]++; vis[j]=1; } } sz++; } } init(); slove(); return 0; }