AGC003 D Anticube

17 篇文章 0 订阅

题意

有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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值