题解:P8778 [蓝桥杯 2022 省 A] 数的拆分

Luogu - P8778 数的拆分

Begining

感觉这题评黄少了,至少得是道绿题。

这篇题解记录了我一下午做题的心路历程,可能有点兀长,也有很多没用的内容,但或许可以加深对题目的理解。

有错误欢迎指出!本蒟蒻一定虚心接受。

提示:题解中有 4 个结论,代表 4 个可行的判定,是有连贯关系的,可以着重理解。

Description

给定 T T T 个正整数 a i a_{i} ai,分别问每个 a i a_{i} ai 能否表示为 a i = x 1 y 1 ⋅ x 2 y 2 ( x 1 , y 1 ∈ N + , y 1 , y 2 ≥ 2 ) a_i={x_1}^{y_1} \cdot {x_2}^{y_2}(x_1,y1 \in \mathbb{N^+},\ y1,y2 \ge 2) ai=x1y1⋅x2y2(x1,y1∈N+, y1,y2≥2) 的形式。

Analysis

首先对 a i a_i ai 进行质因子分解: a i = p 1 c 1 × p 2 c 2 × ⋯ a_i={p_1}^{c_1} \times {p_2}^{c_2} \times \cdots ai=p1c1×p2c2×⋯

然后,我们只需要考虑把 a i a_i ai 的幂次拆成一个 y 1 y_1 y1 的倍数加上 y 2 y_2 y2 的倍数的形式,即对于 a i = p y a_i=p^y ai=py, y = k 1 × y 1 + k 2 × y 2 y = k_1 \times y_1 + k_2 \times y_2 y=k1×y1+k2×y2。

以 5 7 5^7 57 为例,它可以拆成 ( 5 2 ) 2 × ( 5 1 ) 3 (5^2)^2 \times (5^1)^3 (52)2×(51)3,也就是 5 2 × 2 × 5 1 × 3 5^{2 \times 2} \times 5^{1 \times 3} 52×2×51×3,而 2 × 2 + 1 × 3 = 7 2 \times 2 + 1 \times 3=7 2×2+1×3=7。

那么我们只要合理构造出 y 1 y_1 y1 和 y 2 y_2 y2 就行了。在刹那间,你的脑电波和大神欧拉对上了,他给你一条重要信息: 2 , 3 2,3 2,3 是一对万能构造数!

即我们一定可以把 a i a_i ai 的幂次拆成 2 k 1 + 3 k 2 2k_1+3k_2 2k1+3k2 的形式。


简单证明一下:

对于所有偶数,因为偶数一定是 2 2 2 的倍数,所以只要把 k 2 k_2 k2 的系数设为 0 0 0 即可;

对于所有 ≥ 3 \ge 3 ≥3 的奇数,因为奇数 = 偶数 + 奇数,而相邻的奇数之间的最小差值为 2 2 2,所以用 2 k 1 2k_1 2k1 表示偶数部分, 3 k 2 3k_2 3k2 表示奇数部分,一定可以凑成一个奇数。

凑不出来的只有 1 1 1,但这道题里面没有 1 1 1,所以该结论成立。


结论 1

拆分 a i a_i ai 的质因子一定不能有 1 1 1 次幂,这一定凑不出来。

这条结论非常重要,直接决定了我们以后的做题方向。But, T ≤ 1 0 5 , a i ≤ 1 0 18 T \le 10^5,a_i \le 10^{18} T≤105,ai≤1018,直接看有没有 1 1 1 次幂会超时。

那么,我们可以先判断 a i a_i ai 是不是完全平方数或完全立方数,如果是,那么它的幂次一定可以拆成 2 k 1 2k_1 2k1 或 3 k 2 3k_2 3k2 的形式。

若 ⌊ a i ⌋ × ⌊ a i ⌋ = a i \lfloor \sqrt{a_i} \rfloor \times \lfloor \sqrt{a_i} \rfloor = a_i ⌊ai ⌋×⌊ai ⌋=ai,则 a i a_i ai 是完全平方数;

若 ⌊ a i 3 ⌋ × ⌊ a i 3 ⌋ × ⌊ a i 3 ⌋ = a i \lfloor \sqrt[3]{a_i} \rfloor \times \lfloor \sqrt[3]{a_i} \rfloor \times \lfloor \sqrt[3]{a_i} \rfloor = a_i ⌊3ai ⌋×⌊3ai ⌋×⌊3ai ⌋=ai,则 a i a_i ai 是完全立方数。

(这样投机取巧应该也能骗到不少分)

然后,因为现在 a i a_i ai 既不是完全平方数,也不是完全立方数,那么其一定可以表示成 n 2 × m 3 n^2 \times m^3 n2×m3 的形式,其中 n , m > 1 n,m \gt 1 n,m>1。

那它又一定可以转化为 ( n × m ) 2 × m (n \times m)^2 \times m (n×m)2×m。

结论 2:于是乎,我们又有了一个重大发现:

若 a i = n 2 × m 3 a_i=n^2 \times m^3 ai=n2×m3,则分解质因数时筛掉 ≤ n ( n ≤ 1 0 9 ) \le n(n \le 10^9) ≤n(n≤109) 的数后剩下的 a i a_i ai 一定是一个完全平方数 ;分解质因数时筛掉 ≤ m ( m ≤ 1 0 6 ) \le m(m \le 10^6) ≤m(m≤106) 的数后剩下的 a i a_i ai 一定是一个完全立方数

可知 m ≤ a i 3 m \le \sqrt[3]{a_i} m≤3ai ,即 m ≤ 1 0 6 m \le 10^6 m≤106,则又可以枚举 m m m ,这样枚举立方以下的,又能降一点时间复杂度。

But,这样复杂度为 O ( T m ) \text{O}(Tm) O(Tm),超时在所难免。


题目做到这里,基本上人已经懵了。但有句话说得好:"不忘初心,牢记使命!"我们的"初心"是什么?------是找 a i a_i ai 分解质因数后有没有一次方的质因数。我们的"使命"是什么?------是 AC 这道题。

所以,我们不能就此放弃。回想我们最初的目的是找有没有一次方的质因数,那我们就可以把 a i 3 \sqrt[3]{a_i} 3ai 以内(即 1 0 6 10^6 106 以内)的质数先筛出来。

那它剩下的质因数一定都是 > 1 0 6 \gt 10^6 >106 的数,大于 a i = 1 0 9 \sqrt{a_i}=10^9 ai =109 的质因数一定最多只有 1 1 1 个(它自然是一次的),那么在 1 0 6 ∼ 1 0 9 10^6 \sim 10^9 106∼109 这个区间里的质因子就最多有 2 2 2 个。

结论 3

如果有 > 1 0 9 \gt 10^9 >109 的质因子,那么它一定是一次方的,这就找到了。


那除了上述之外,还能怎么优化呢?

再回到最初的起点,是一个 n 2 × m 3 n^2 \times m^3 n2×m3 的情况,根据我们敏锐的数感,聪明的你会发现,这个 n n n 一定是很小的,因为过大的 n n n 一定组不成这种形式。

假如 n = m = 1000 n= m = 1000 n=m=1000,那么 100 0 2 × 100 0 3 = 1 0 15 1000^2 \times 1000^3=10^{15} 10002×10003=1015,已经很大了;当 n = m = 3000 n=m=3000 n=m=3000 时, 300 0 2 × 300 0 3 = 2.43 × 1 0 17 3000^2 \times 3000^3=2.43 \times 10^{17} 30002×30003=2.43×1017,已经十分接近。

所以 n = m = 4000 n=m=4000 n=m=4000 的时候, n 2 × m 3 n^2 \times m^3 n2×m3 一定 > 1 0 18 \gt 10^{18} >1018。

结论 4

n 2 × m 3 n^2 \times m^3 n2×m3 中 n , m n,m n,m 最多只有 4000 4000 4000。


正解:

综上,只要去枚举 4000 4000 4000 以内的质数(结论 4),看里面有没有是一次方的质因数(结论 1);同时把质因数给 a i a_i ai 除进去,看最后剩下的 a i a_i ai 是不是完全平方数或完全立方数即可(结论 2)。

这样去枚举,则不算预处理素数的复杂度是 O ( T ∑ i = 1 T a i ) \text{O}(T \sum\limits_{i=1}^{T} \sqrt{a_i}) O(Ti=1∑Tai ),可以通过。

Code

注意系统自带的 sqrt 函数和 cbrt 函数可能存在精度误差(见 P10373立方根 题目背景)。

在 P10373 中给出了修复方法,但那不是很准确。发现整型向下取整误差一定不超过 1 1 1,所以可以把分解完质因数后的 a i a_i ai 分别尝试 a i ± 1 a_i \pm 1 ai±1 是否可行,以此修复误差。

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxp = 1000;

int vis[maxp + 5], prime[maxp + 5], len;

// 用埃氏筛筛出1000以内的素数
void getPrime()
{
    for(int i = 2; i <= maxp; i ++){
        if(!vis[i]) prime[len ++] = i;
        for(int j = 2 * i; j <= maxp; j += i) vis[j] = 1;
    }
}

// 判断x是不是完全平方数或完全立方数
bool check(ll x)
{
    ll sq = sqrtl(x);
    if(sq*sq==x || (sq+1)*(sq+1)==x || (sq-1)*(sq-1)==x){ // 避免有误差,这个误差确定不超过1
        return true;
    }
    
    ll cb = cbrtl(x);
    if(cb*cb*cb==x || (cb+1)*(cb+1)*(cb+1)==x || (cb-1)*(cb-1)*(cb-1)==x){
        return true;
    }
    
    return false;
}

void solve()
{
    ll a;
    cin >> a;
    
    // 是完全平方数或完全立方数直接yes
    if(check(a)){
        cout << "yes\n";
        return ;
    }
    
    // 下面分解质因数
    bool flag = true;
    for(int i = 0; i < len; i ++){
        int cnt = 0;
        int p = prime[i];
        while(a % p == 0){
            cnt ++;
            a /= p;
        }
        // 出现1次方,不合法
        if(cnt == 1){
            cout << "no\n";
            return ;
        }
    }
    
    // 最后再判断一遍分解完的即可
    if(check(a)) cout << "yes\n";
    else cout << "no\n";
}

signed main()
{
    ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
    getPrime();
    int T; cin >> T;
    while(T --) solve();
    return 0;
}

Ending

这里是 YLCHUP,谢谢大家!。

相关推荐
LCG元1 小时前
【面试问题】JIT 是什么?和 JVM 什么关系?
面试·职场和发展
唐诺3 小时前
几种广泛使用的 C++ 编译器
c++·编译器
XH华3 小时前
初识C语言之二维数组(下)
c语言·算法
南宫生4 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
不想当程序猿_4 小时前
【蓝桥杯每日一题】求和——前缀和
算法·前缀和·蓝桥杯
sanguine__4 小时前
Web APIs学习 (操作DOM BOM)
学习
落魄君子4 小时前
GA-BP分类-遗传算法(Genetic Algorithm)和反向传播算法(Backpropagation)
算法·分类·数据挖掘
冷眼看人间恩怨4 小时前
【Qt笔记】QDockWidget控件详解
c++·笔记·qt·qdockwidget
菜鸡中的奋斗鸡→挣扎鸡4 小时前
滑动窗口 + 算法复习
数据结构·算法
红龙创客4 小时前
某狐畅游24校招-C++开发岗笔试(单选题)
开发语言·c++