【题解 CF840C & P4448】 On the Bench & 球球的排列

发布时间 2023-10-18 20:37:33作者: dijah

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\) 个位置上,有:

\[f_{i,j,0}=f_{i-1,u,j-u} \times (i-j) , u \in [0,j] \]

2.第 \(i\) 位颜色与第 \(i-1\) 种颜色不同,放到两种相同颜色的中间。

\(k\) 照旧为 \(0\) ,枚举非上一种颜色的冲突数,每次可放到 \(j+1\) 个位置上,有:

\[f_{i,j,0}=f_{i-1,u,j-u+1} \times (j+1) , u \in [0,j+1] \]

所以第 \(i\) 位颜色与第 \(i-1\) 种颜色不同时,状态转移方程为:

\[f_{i,j,0} = \begin{cases} f_{i-1,u,j-u} \times (i-j) , u \in [0,j] \\ f_{i-1,u,j-u+1} \times (j+1) , u \in [0,j+1] \end{cases} \]

3.第 \(i\) 位颜色与第 \(i-1\) 种颜色相同,放到与第 \(i\) 为颜色的数旁边。

设第 \(i\) 种颜色已经放了 \(q\) 个。
\(j\) 不变,\(k\) 增加 \(1\),可放到 \(2 \times q - (k-1) =2 \times q - k + 1\) 个位置上,有:

\[f_{i,j,k} = f_{i-1,j,k-1} \times (2 \times q-k+1), k\in [1,q] \]

4.第 \(i\) 位颜色与第 \(i-1\) 种颜色相同,放到两种与第 \(i\) 种颜色的数的中间。

\(j\)\(1\)\(k\) 不变,可放到 \(j+1\) 个位置上,有:

\[f_{i,j,k} = f_{i-1,j+1,k} \times (j+1) ,k \in [0,q] \]

5.第 \(i\) 位颜色与第 \(i-1\) 种颜色相同,放到两个异色且都不与第 \(i\) 位颜色相同的数的中间。

\(j\) 不变,\(k\) 也不变,可放到 \(((i-1)+1)-j-(2 \times q-k) =i-j-2 \times q + k\) 个位置上,有:

\[f_{i,j,k} = f_{i-1,j,k} \times (i-j-2 \times q + k) , k \in [0,q] \]

所以第 \(i\) 位颜色与第 \(i-1\) 种颜色相同时,状态转移方程为:

\[f_{i,j,k} = \begin{cases} f_{i,j,k} = f_{i-1,j,k-1} \times (2 \times q-k+1), k\in [1,q] \\ f_{i,j,k} = f_{i-1,j+1,k} \times (j+1) ,k \in [0,q] \\ f_{i,j,k} = f_{i-1,j,k} \times (i-j-2 \times q + k) , k \in [0,q] \end{cases} \]

时间复杂度 \(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;
}