题意:
给你n个数,求一个最大的集合,使得集合内的数两两相乘都不是完全立方数,输出集合大小。
n
<
=
1
e
5
,
a
i
<
=
1
e
10
n<=1e5,a_i<=1e10
n<=1e5,ai<=1e10
题解:
这种题经常会需要从质因数角度考虑。我们不难想到,如果一个数是完全立方数的话,那么它的每一个质因子的次数应该都是3的倍数。有一个想法是一个数的每一个质因子次数其实只需要这些次数%3之后的结果,得到同一个结果的数归为一类,对于同一类数,与之相对不能同时选的数是相同的。这样我们可以求出什么样的数与这一类数相乘会变成各个质因数次数都是3的数,求法是让两个数对应的质因子次数之和%3=0。
这样我们可以用map来记录每类数出现的个数,然后在当前这一类和与之相对的那一类数中贪心地选择数多的加入集合,注意要特判1,因为同时只能加入一个1。
这道题还有一个黑科技,我们如果处理到
1
e
10
\sqrt{1e10}
1e10的质因数的话判断会超时。于是在预处理质因数的时候我们只预处理到
1
e
10
3
\sqrt[3]{1e10}
31e10,如果不能被处理出的这些质数除尽,那么只可能是剩余了一个大质数或者一个质数的平方。分类讨论一些就好了。
update:还可能是两个不同的质数相乘,代码懒得改了,反正水过去了。。。
代码:
#include <bits/stdc++.h>
using namespace std;
int n,p[100010],cnt,vis[100010];
long long a[100010],l[100010],r[100010],ans;//lΪÔÊý£¬rΪÓël³Ë»ýΪÍêÈ«Á¢·½ÊýµÄÊý
map<long long,int> mp;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%lld",&a[i]);
vis[1]=1;
for(int i=2;i<=2200;++i)
{
if(!vis[i])
p[++cnt]=i;
for(int j=1;j<=cnt&&p[j]*i<=2200;++j)
{
vis[p[j]*i]=1;
if(i%p[j]==0)
break;
}
}
for(int i=1;i<=n;++i)
{
long long ji=a[i],qwq=1,ovo=1;
for(int j=1;j<=cnt;++j)
{
int gg=0;
while(ji%p[j]==0)
{
++gg;
ji/=p[j];
}
gg%=3;
if(gg)
{
for(int k=gg;k<3;++k)
{
qwq*=p[j];
if(qwq>1e10)
qwq=0;
}
while(gg)
{
ovo*=p[j];
--gg;
}
}
}
l[i]=ovo*ji;
mp[l[i]]++;
r[i]=qwq;
long long qaq=(long long)sqrt(ji);
if(qaq*qaq!=ji)
{
if(ji*r[i]<=1e10)
r[i]*=ji;
else
r[i]=0;
if(ji*r[i]<=1e10)
r[i]*=ji;
else
r[i]=0;
}
else
{
if(qaq*r[i]<=1e10)
r[i]*=qaq;
else
r[i]=0;
}
}
for(int i=1;i<=n;++i)
{
if(l[i]==1)
continue;
ans+=max(mp[l[i]],mp[r[i]]);
mp[l[i]]=0;
mp[r[i]]=0;
}
if(mp[1])
ans++;
printf("%lld\n",ans);
return 0;
}