1. 程式人生 > >luogu P4446 [AHOI2018初中組]根式化簡

luogu P4446 [AHOI2018初中組]根式化簡

背景

A H O I AHOI 竟然有初中組的良心設定,專門即將在初中退役的我找回了一點自信。

題目傳送門:

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

題意:

求正整數 a

, b a,b ,使其滿足 a 3 b =
n a^3*b=n
的最大的 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 分。

考慮優化。
發現我們只用列舉質數作為 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 分。

再考慮優化。
時間複雜度已是 Θ ( T k ) \Theta(T*k) ,其中 k k 是一個大常數。
不妨考慮縮小 k k 的值,發現最後的數可能比較大,可以用二分來實現最後求做完後的 n n 是否是 3 3 次方數。
發現 5000 0 3 = 1.25 1 0 14 50000^3=1.25*10^{14} ,略小於 1 0 18 10^{18} ,但因為後面有一個二分判斷(相當於再乘上一個 50000 50000 ,從而超過 1 0 18 10^{18} ),已經不影響結果。因此正確性可以證明。

#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));
	}
}