题意
有n个数,从中选出m个数,使得这m个数两两相乘的乘积都不是立方数
n<=1e5
题解
这种题很大部分都需要质因数分解…
然而质因数分解就T了…
我们暂且不管,考虑一下质因数分解后的做法
我们把所有的指数%3,这样就组合成了一个新数,然后用map把相同的归成一类。
我们先把1特判掉,然后所有的矛盾关系应该都是两两的,我们比较矛盾的两个的大小,取数目较大的那一个加入答案
这样就在
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)解决了问题
然而问题还没有解决,现在考虑质因数分解怎么做
我们先线性筛处理一下1e5以内的素数,同时预处理质数的平方
对于每个待分解的数x,我们暂时只寻找
x
3
\sqrt[3]{x}
3x以内的素数进行试除
然后呢,显然,剩下的x若>1,则要么是质数,要么是某个质数的平方
然后我们就完成了质因数分解,于是这道题就彻底解决了
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
const int N=1e5+5;
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pll;
typedef map<ll,int>::iterator miter;
int n,p[N+5],pcnt;
bool vis[N+5];
map<ll,int> cnt,calc;
map<ll,ll> inver;
void Init()
{
for(int i=2;i<=N;i++)
{
if(!vis[i])
p[++pcnt]=i,calc[1ll*i*i]=i;
for(int j=1;i*p[j]<=N;j++)
{
vis[i*p[j]]=true;
if(i%p[j]==0)
break;
}
}
}
int main()
{
//freopen("anticube.in","r",stdin);
//freopen("anticube.out","w",stdout);
scanf("%d",&n);//
Init();
int ans=0;
for(int i=1;i<=n;i++)
{
ll a,tmp,h=1,inv=1;
scanf("%lld",&a);
tmp=a;
for(int j=1;j<=pcnt&&1ll*p[j]*p[j]*p[j]<=a;j++)
if(tmp%p[j]==0)
{
int xcnt=0;
while(tmp%p[j]==0)
tmp/=p[j],xcnt++;
xcnt%=3;
int xxcnt=xcnt;
while(xxcnt--)
h*=p[j];
for(int i=1;i<=(3-xcnt)%3;i++)
inv*=p[j];
}
if(tmp>1)
{
h*=tmp;
miter it=calc.find(h);
if(it!=calc.end())
inv*=it->second;
else
inv*=tmp*tmp;
}
cnt[h]++;
inver[h]=inv,inver[inv]=h;
}
if(cnt.find(1)!=cnt.end()){
ans++;
cnt.erase(1);
}
for(miter it=cnt.begin();it!=cnt.end();it++)
if(cnt.find(inver[it->first])==cnt.end())
ans+=it->second;
else
{
ans+=max(it->second,cnt[inver[it->first]]);
it->second=cnt[inver[it->first]]=0;
}
printf("%d\n",ans);
}