蓝桥云课 数的拆分(数论)

题目链接

觉得这个题挺好的,留个档,至于题解在题目上已经有讲的很好的了。


思路:

数学思维题。

对一个数 x x x,根据唯一分解定理可以拆成 x = p 1 k 1 ∗ p 2 k 2 ∗ p 3 k 3 ∗ ⋯ ∗ p s k s x=p_1^{k_1}*p_2^{k_2}*p_3^{k_3}*\dots *p_s^{k_s} x=p1k1p2k2p3k3psks的形式,然后我们要把这个形式转化为题目上 x = q 1 y 1 ∗ q 2 y 2 x=q_1^{y_1}*q_2^{y_2} x=q1y1q2y2 的形式。

其实比较容易发现内部的 q 1 , q 2 q_1,q_2 q1,q2 还是可以再拆的,因此 y 1 , y 2 y_1,y_2 y1,y2 我们选择尽可能小的数会比较好凑。比如说 q 6 q^6 q6 ( q 3 ) 2 (q^3)^2 (q3)2 是完全相同的,但是后者的 y = 2 y=2 y=2 就比较好凑,指数为 2 2 2 的倍数的情况显然更加常见。因此我们选择让 y 1 = 2 , y 2 = 3 y_1=2,y_2=3 y1=2,y2=3

这样,对一个质因数 p i k i p_i^{k_i} piki,我们需要 q 1 y 1 ∗ q 2 y 2 = q 1 2 ∗ q 2 3 q_1^{y_1}*q_2^{y_2}=q_1^{2}*q_2^{3} q1y1q2y2=q12q23 包含它,假设 q 1 q_1 q1 x x x p i p_i pi q 2 q_2 q2 y y y p i p_i pi,那么就有 k i = 2 ∗ x + 3 ∗ y k_i=2*x+3*y ki=2x+3y。而这个式子在 k i > 1 k_i>1 ki>1 时是总是有解的(非负整数解)。换句话说,只要 k i > 1 k_i>1 ki>1 p i k i p_i^{k_i} piki 就有办法分到 q 1 q_1 q1 q 2 q_2 q2 中去,否则就没可能。因此我们只要保证所有质因数都满足它的指数大于 1 1 1,那么就存在一种分配方案,把 x x x 可以转化为 q 1 y 1 ∗ q 2 y 2 q_1^{y_1}*q_2^{y_2} q1y1q2y2 的形式。

不过要分解质因数,我们需要筛到 1 0 18 = 1 0 9 \sqrt{10^{18}}=10^9 1018 =109 以内的质因数,预处理就会超时。而且每个数我们都要分解一下,分解的时候需要枚举质因数,质因数的个数就不能太多。

考虑到当 x x x 转化为 q 1 y 1 ∗ q 2 y 2 = q 1 2 ∗ q 2 3 q_1^{y_1}*q_2^{y_2}=q_1^{2}*q_2^{3} q1y1q2y2=q12q23 的形式时,当 q 1 = 1 q_1=1 q1=1 q 2 = 1 q_2=1 q2=1 时,这个数就是个纯粹的平方数或者立方数,直接特判就行了。

q 1 , q 2 ≠ 1 q_1,q_2\not=1 q1,q2=1 时,这个数 x x x 就至少拥有了 5 5 5 个质因数。此时除了最大的质因数,其他较小的质因数一定都满足 ≤ 1 0 18 5 ≈ 3981 \le\sqrt[5]{10^{18}}\approx 3981 51018 3981,剩下的那个较大的质因数 q q q 只有可能是 q 1 , q 2 , q 3 , q 4 q^1,q^2,q^3,q^4 q1,q2,q3,q4(再多 它的大小就小于 1 0 18 5 \sqrt[5]{10^{18}} 51018 了),除开指数是 1 1 1 的情况,其他情况就是个平方数或者立方数,可以直接特判。

code:

#include <iostream>
#include <cstdio>
#include <map>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;

ll prime[maxn],cnt;
bool vis[maxn];
void Eular(int n){
	vis[1]=true;
	for(int i=2;i<=n;i++){
		if(!vis[i])prime[++cnt]=i;
		for(int j=1,p=prime[1];j<=cnt && i*p<=n;p=prime[++j]){
			vis[i*p]=true;
			if(i%p==0)break;
		}
	}
}

ll T,a;

bool is2(ll x){
	ll l=1,r=x,mid;
	while(l<r){
		mid=l+r>>1;
		if(mid<=1e9 && mid*mid<x)l=mid+1;
		else r=mid;
	}
	return l*l==x;
}
bool is3(ll x){
	ll l=1,r=x,mid;
	while(l<r){
		mid=l+r>>1;
		if(mid<=1e6 && mid*mid*mid<x)l=mid+1;
		else r=mid;
	}
	return l*l*l==x;
}

bool solve(ll a){
	if(is2(a) || is3(a)){
		return true;
	}
	for(int i=1,p=prime[1],c;i<=cnt && p<=a;p=prime[++i]){
		if(a%p==0){
			c=0;
			while(a%p==0){
				a/=p;
				c++;
			}
			if(c==1){
				return false;
			}
		}
	}
	if(is2(a) || is3(a))return true;
	else return false;
}

int main(){
	Eular(5000);
	cin>>T;
	while(T--){
		cin>>a;
		puts((solve(a))?"yes":"no");
	}
	return 0;
} 
  • 26
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值