luogu P4446 [AHOI2018初中组]根式化简

背景

A H O I AHOI AHOI竟然有初中组的良心设置,专门即将在初中退役的我找回了一点自信。

题目传送门:

https://www.luogu.org/problemnew/show/P4446

题意:

求正整数 a , b a,b a,b,使其满足 a 3 ∗ b = n a^3*b=n a3b=n的最大的 a a a

思路 & \& &代码:

考虑纯暴力。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define LL long long
using namespace std;
	LL n;
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%lld",&n);
		LL ans=1,p=sqrt(n);
		for(LL i=2;i<=p;i++)
		{
			int tot=0;
			while(!(n%i)) n/=i,tot++;
			if(tot>=3) ans*=pow(i,(int)(tot/3));
			if(n==1) break;
		}
		printf("%lld\n",ans);
	}
}

轻松 60 60 60分。

考虑优化。
发现我们只用枚举质数作为 a a a即可,由唯一分解定理可知任意的合数 a a a都能分为若干个质数的乘积,最后再相乘即可。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define LL long long
using namespace std;
	LL n;
	int t=0;
	int prime[1000010];
	bool bz[1000010];
void init(int ma)
{
	bz[0]=bz[1]=true;
	for(int i=2;i<=ma;i++)
	{
		if(!bz[i]) prime[++t]=i;
		for(int j=1;j<=t&&i*prime[j]<=ma;j++)
		{
			bz[i*prime[j]]=true;
			if(!(i%prime[j])) break;
		}
	}
}
int main()
{
	int T;
	scanf("%d",&T);
	init(1000000);
	while(T--)
	{
		LL ans=1;
		scanf("%lld",&n);
		for(int i=1;i<=t;i++)
		{
			int tot=0;
			if((LL)prime[i]*prime[i]*prime[i]>n) break;
			while(!(n%prime[i])) n/=prime[i],tot++;
			ans*=pow(prime[i],(int)tot/3);
		}
		printf("%lld\n",ans);
	}
}

轻松 80 80 80分。

再考虑优化。
时间复杂度已是 Θ ( T ∗ k ) \Theta(T*k) Θ(Tk),其中 k k k是一个大常数。
不妨考虑缩小 k k k的值,发现最后的数可能比较大,可以用二分来实现最后求做完后的 n n n是否是 3 3 3次方数。
发现 5000 0 3 = 1.25 ∗ 1 0 14 50000^3=1.25*10^{14} 500003=1.251014,略小于 1 0 18 10^{18} 1018,但因为后面有一个二分判断(相当于再乘上一个 50000 50000 50000,从而超过 1 0 18 10^{18} 1018),已经不影响结果。因此正确性可以证明。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define LL long long
using namespace std;
	LL n;
	int t=0;
	int prime[50010];
	bool bz[50010];
void init(int ma)
{
	bz[0]=bz[1]=true;
	for(int i=2;i<=ma;i++)
	{
		if(!bz[i]) prime[++t]=i;
		for(int j=1;j<=t&&i*prime[j]<=ma;j++)
		{
			bz[i*prime[j]]=true;
			if(!(i%prime[j])) break;
		}
	}
}
LL check(LL x)
{
	LL l=1,r=1000000,mid;
	while(l<=r)
	{
		mid=(l+r)>>1;
		if(mid*mid*mid==x) return mid;
		if(mid*mid*mid<x) l=mid+1; else r=mid-1;
	}
	if(l*l*l==x) return l;
	return false;
}
int main()
{
	int T;
	scanf("%d",&T);
	init(50000);
	while(T--)
	{
		LL ans=1;
		scanf("%lld",&n);
		for(int i=1;i<=t;i++)
		{
			int tot=0;
			if((LL)prime[i]*prime[i]*prime[i]>n) break;
			while(!(n%prime[i])) n/=prime[i],tot++;
			ans*=pow(prime[i],(int)tot/3);
		}
		LL o=check(n);
		printf("%lld\n",ans*(!o?1:o));
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值