题意
给你一个长度为\(n\)的序列,你需要把这个序列分成若干段,使得每一段满足:从这一段中任意选择两个数,使得这两个数的乘积不为完全平方数。最小化分的段数,问你最少分成多少段。
分析
发现完全平方数其实就是质因数分解之后每一个质因子的幂次都为偶数的数,那么只要有一个质因子的幂次为奇数,这个数就不是完全平方数。所以对于一个数的一些幂次为奇数的因子,必须要有一个数,这个数的所有幂次为奇数的因子与他完全一致,这两个数才能凑成完全平方数。所以我们就有一个暴力的做法:我们从左往右处理每一个数字,每遇到一个数字我们就把这个数字质因数分解。然后我们把它的幂次为偶数的质因子忽略,只考虑它幂次为奇数的质因子,如果它之前存在一个数,那个数的奇数次质因子集合与它完全相同,那么这两个数显然不能放在同一段里,也就是需要贪心的分成两段,如果没有,那么就把这个数加入之前的那一段然后把这个数的质因子记录下来。
考虑怎么实现,发现需要一个可以支持插入和查询一个集合是否存在,可以用哈希,但是我们发现,考虑所有的质数,如果他们不同,那么他们的乘积也肯定不同,所以我们只需要用乘积来代表一个质因数的集合然后用map维护即可。
总复杂度为\(O(n\sqrt{INT\_MAX)}\),但是能过(
代码
/* Creat on Xishui's iPad
Author: Xishui
date: 21/10/21
Language: C++
*/
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int n,k;
int devide(int x){
int ret=1;
if(x==1)
return 1;
for(int i=2;i*i<=x;i++){
int cnt=0;
while(x%i==0){
x/=i;
cnt++;
}
if(cnt%2)
ret*=i;
}
return ret*x;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&k);
int ans=0;
map<int,int>ret;
for(int i=1;i<=n;i++){
int x;
scanf("%d",&x);
int res=devide(x);
if(ret[res]){
ans++;
ret.clear();
}
ret[res]=1;
}
printf("%d\n",ans+1);
}
return 0;
}