【题解 CF840C & P4448】 On the Bench & 球球的排列
On the Bench
题面翻译
给定一个序列 \(a(a_i\le 10^9)\),长度为 \(n(n\le 300)\)。
试求有多少 \(1\) 到 \(n\) 的排列 \(p_i\),满足对于任意的 \(2\le i\le n\) 有 \(a_{p_{i-1}}\times a_{p_i}\) 不为完全平方数,答案对 \(10^9+7\) 取模。
题目描述
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 $ a_{pi}·a_{pi+1} $ is not perfect square, holds. Leha wants to find number of right permutations modulo $ 10^{9}+7 $ .
输入格式
First line of input data contains single integer $ n $ ( $ 1<=n<=300 $ ) — length of the array.
Next line contains $ n $ integers $ a_{1},a_{2},...\ ,a_{n} $ ( $ 1<=a_{i}<=10^{9} $ ) — found array.
输出格式
Output single integer — number of right permutations modulo $ 10^{9}+7 $ .
样例 #1
样例输入 #1
3
1 2 4
样例输出 #1
2
样例 #2
样例输入 #2
7
5 2 4 2 4 1 1
样例输出 #2
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_i,a_j,a_u\) , \(a_i \times a_j\) 为完全平方数, \(a_i \times a_u\) 为完全平方数,显然有 \(a_j \times a_u\) 为完全平方数。
根据这条性质,那么,对于一堆数 \(a_1,a_2,a_3,...,a_m\) 中 \(a_1 \times a_2,a_2\times a_3,...,a_{m-1} \times a_m\) 为完全平方数,这堆数中任意两个数相乘都为完全平方数。
所以,我们考虑将数变成几堆,每堆内的数相乘为完全平方数,每堆中的数与他堆中的数不为完全平方数,也就可以放在一起。
用二分来求每两个数相乘是否为完全平方数,将相乘为完全平方数的放成一堆,给每堆里的数染色,相同堆染同色,问题变成:将一些数放成一排,且相邻颜色不同的方案数?
由于 \(n\le 300\) ,考虑 \(DP\) 。
按颜色大小从小到大放,放成像 \(1,1,1,2,2,3,...\) 这样的形式。
所以考虑 \(f_{i,j,k}\) 表示放到第 \(i\) 位,非当前颜色的冲突数 \(j\) ,当前颜色的冲突数 \(k\) 。
冲突数指的就是颜色相同的放成相邻的个数,例如 \(3,3,3\) 有 \(2\) 个冲突。
很显然,初始化 \(f_{0,0,0} =1\) ,求 \(f_{n,0,0}\) 。
一种颜色一种颜色的放,采取插入 \(DP\) 的形式 ,将放完的紧密的靠在一起,就会出现有冲突的,再一步步推到没冲突的。
考虑状态转移方程,分情况讨论:
1.第 \(i\) 位颜色与第 \(i-1\) 种颜色不同,放到两种不同颜色的中间。
所以 \(k\) 必为 \(0\) ,枚举非上一种颜色的冲突数 \(i\),每一次可放到 \(((i-1)+1)-j=i-j\) 个位置上,有:
2.第 \(i\) 位颜色与第 \(i-1\) 种颜色不同,放到两种相同颜色的中间。
\(k\) 照旧为 \(0\) ,枚举非上一种颜色的冲突数,每次可放到 \(j+1\) 个位置上,有:
所以第 \(i\) 位颜色与第 \(i-1\) 种颜色不同时,状态转移方程为:
3.第 \(i\) 位颜色与第 \(i-1\) 种颜色相同,放到与第 \(i\) 为颜色的数旁边。
设第 \(i\) 种颜色已经放了 \(q\) 个。
\(j\) 不变,\(k\) 增加 \(1\),可放到 \(2 \times q - (k-1) =2 \times q - k + 1\) 个位置上,有:
4.第 \(i\) 位颜色与第 \(i-1\) 种颜色相同,放到两种与第 \(i\) 种颜色的数的中间。
\(j\) 减 \(1\) ,\(k\) 不变,可放到 \(j+1\) 个位置上,有:
5.第 \(i\) 位颜色与第 \(i-1\) 种颜色相同,放到两个异色且都不与第 \(i\) 位颜色相同的数的中间。
\(j\) 不变,\(k\) 也不变,可放到 \(((i-1)+1)-j-(2 \times q-k) =i-j-2 \times q + k\) 个位置上,有:
所以第 \(i\) 位颜色与第 \(i-1\) 种颜色相同时,状态转移方程为:
时间复杂度 \(O(n^3)\) ,空间复杂度 \(O(n^3)\) ,使用滚动数组可优化至 \(O(n^2)\) 。
Code
#include<bits/stdc++.h>
using namespace std;
const long long mod=1e9+7;
long long n,f[2][305][305],a[305],d[305],p=0;
bool check(long long x)
{
long long l=1,r=1e9,mid;
while(l<=r)
{
mid=(l+r)>>1;
if(mid*mid==x)return true;
if(mid*mid<x)l=mid+1;
else r=mid-1;
}
return false;
}
int main()
{
long long q=0;
scanf("%lld",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
}
for(int i=1;i<=n;i++)
{
for(int j=i+1;j<=n;j++)
{
if(check(a[i]*a[j]))
{
if(!d[i])d[i]=++q;
d[j]=d[i];
}
}
}
for(int i=1;i<=n;i++)a[i]=d[i]>0?d[i]:(++q);
q=0;
sort(a+1,a+n+1);
f[0][0][0]=1;
for(int i=1;i<=n;i++)
{
p^=1;
if(a[i]!=a[i-1])
{
q=1;
for(int j=0;j<=i-1;j++)
{
for(int u=0;u<=j;u++)f[p][j][0]+=(f[p^1][u][j-u]*(i-j)+f[p^1][u][j-u+1]*(j+1))%mod,f[p][u][j-u]%=mod;
f[p][j][0]+=f[p^1][j+1][0]*(j+1);
f[p][j][0]%=mod;
}
}
else
{
for(int j=0;j<=i-1;j++)
{
for(int u=0;u<=i-1;u++)
{
if(u>=1)f[p][j][u]+=f[p^1][j][u-1]*(2*q-u+1)%mod;
f[p][j][u]+=f[p^1][j+1][u]*(j+1)%mod;
f[p][j][u]+=f[p^1][j][u]*(i-j-2*q+u)%mod;
f[p][j][u]%=mod;
}
}
q++;
}
for(int j=0;j<=i;j++)
{
for(int u=0;u<=i;u++)f[p^1][j][u]=0;
}
}
cout<<f[n&1][0][0];
return 0;
}