XTU OJ 2024 下学期作业 0x08 - 素数判定,分解因式

Reference


A. XTUOJ 1121 欧拉函数

分解质因数,模拟


  • 利用分解质因数得到 n n n 的所有质因子再将其带入欧拉函数的计算公式即可
  • 分解质因数的朴素算法如下
    for (int i = 2; i * i <= N; ++i) {
    	if (N % i == 0) {         // 如果 i 能整除 N,说明 i 是 N 的一个质因子
    		res[size++] = i;
    		while (N % i) N /= i;
    	}
    }
    if (N != 1) res[size++] = N;  // 最后若 N 不等于 1,说明留下了一个素数
    
  • 下面我们来证明上面算法的正确性:
    我们用 N N N 来表示还进行质因数分解的初始值,N 表示代码执行过程中的 N N N
    ➀ 首先考察 N 的变化。当 for 循环进行到某个 i(开始时有 i 整除 N)结束时,必然已经执行完了 while (N % i) N/= i;,因此此时 i 不能整除 N,但 N 整除 N N N 仍成立。由此我们有:当循环进行到 i 开始时,N 整除 N N N 且不被任何小于 i 的整数(不考虑 1 1 1)整除。
    ② 其次证明 res 内的元素都为 N N N 的因子。for 循环内 i 加入 res 的条件是 N % i == 0;,因此 for 循环内纳入的 i 必然都是 N N N 的因子,对于 for 循环结束后得到的 N,如果它不为 1 1 1,由 ➀ 可知它必然是 N N N 的因子。
    ③ 然后证明 res 内的元素都是素数。假设 res 内存在一个合数 K K K,必然有 k ≤ K k \le \sqrt{K} kK 满足 k k k 整除 K K K ,这样的 K K K 不可能做为某个 i 被加入 res:当循环进行到 K K K 时, k k k 整除 K \sqrt{K} K 可以推出 k k k 也整除 N,但由 ➀ 可知 N 不被任何小于 K K K 的数整除,与假设矛盾;这样的 K K K 不可能作为最后的 N 加入 res:循环退出的条件是 i * i > N,因此已经遍历完所有不超过 K \sqrt{K} K i,即最后的 N 不可能还有因子 k k k,与假设矛盾。
    ④ 最后证明 N N N 所有的质因子必然出现在 res 中。假设 p p p N N N 的一个质因子但不在 res 中,显然 p p p 不可能是 for 循环中的一个 i,那么它必然整除最后的 N,所以此时最后的 N 必大于 1 1 1,由 ③ 可知最后的 N 若大于 1 1 1 必为素数,因此此时最后的 N 等于此 p p p,必会加入 res,与假设矛盾。

核心代码

void A_1008_solve () {
    ans = n;
    if (n == 1) ans = 0;
    for (int i = 2; i * i <= n; ++i) {
        if (n % i == 0) {
            ans = ans / i * (i - 1);    // 先除后乘避免溢出
            while (n % i == 0) n /= i;
        }
    }
    if (n > 1) ans = ans / n * (n - 1);
}

B. XTUOJ 1295 Flawless Prime

素性检测,模拟


  • 模拟去掉最高位的过程,判断结果是否为素数即可
  • 简单素性检测
    bool is_prime (int n) {
        if (n < 2) return false;
        for (int i = 2; i * i <= n; ++i)
            if (n % i == 0) return false;
        return true;
    }
    
  • 上面的素性检测基于这样一个事实:合数 N N N 的因子总是成对存在,设 x x x N N N 的一个因子,则 x N \dfrac{x}{N} Nx 也是 N N N 的因子,且 x x x x N \dfrac{x}{N} Nx 中必有一个不大于 N \sqrt{N} N (若两个都大于 N \sqrt{N} N ,它们的积会大于 N N N,显然不可能)。因此对于数 N N N,素性检测只需检查它是否存在小于 N \sqrt{N} N 的因子即可。

核心代码

void B_1008_solve () {
    ans = false;
    int cnt = 0;
    for (int nCopy = n; nCopy > 0; nCopy /= 10) {
        if (nCopy % 10 == 0) return ;     // 有前导 0 直接 return
        digit[cnt++] = tmp % 10;
    }
    while (cnt--) {
        if (!is_prime(n)) return ;
        n -= digit[cnt] * pow_of_ten[cnt];
    }
    ans = true;
}

C. XTUOJ 1332 素数

素性检测,枚举


  • 按题意枚举所有区间再判断是否为素数即可

核心代码

void C_1008_solve () {
    ans = 0;
    int len = strlen(n);
    for (int left = 0; left < len; ++left) {
        for (int right = left; right < len; ++right) {
            int numFromInterval = 0;
            for (int i = left; i <= right; ++i) {
                if (i > left) numFromInterval *= 10;
                numFromInterval = numFromInterval + n[k] - '0';
            }
            if (is_prime(numFromInterval)) ++ans;
        }
    }
}

D. XTUOJ 1524 不定方程的正整数解

分解质因数,数论函数


  • 1 x − 1 y = 1 a \dfrac{1}{x} - \dfrac{1}{y} = \dfrac{1}{a} x1y1=a1 可以写成 ( a + y ) ( a − x ) = a 2 (a + y)(a - x) = a^2 (a+y)(ax)=a2,因此原问题可以转化为求 a 2 a^2 a2 有多少对不相等的因子
  • 任意整数 N N N 的因子数个数可以由函数 τ ( n ) = ( e 1 + 1 ) ( e 2 + 1 ) … ( e k + 1 ) \tau(n) = (e_1 + 1)(e_2 + 1)\dots(e_k + 1) τ(n)=(e1+1)(e2+1)(ek+1),其中 e 1 , e 2 , … , e k e_1, e_2, \dots ,e_k e1,e2,,ek 来自于整数的唯一分解: N = p 1 e 1 p 2 e 2 … p k e k N = p_1^{e_1}p_2^{e_2} \dots p_k^{e_k} N=p1e1p2e2pkek 中的指数。这个结论在整数唯一分解的基础上可以很容易由乘法计数原理得到

核心代码

void D_1008_solve () {
    ans = 1;
    for (int i = 2; i * i <= a; ++i) {
        if (a % i == 0) {
            int e = 0;
            for (; a % i == 0; ++e) a /= i;
            ans *= 2 * e + 1;
        }
    }
    if (a != 1) ans *= 3;   // a != 1 表示最后还余下一个质因子,指数为 1
    ans = (ans - 1) / 2;    // 去除 a * a = a ^ 2 的情况后除 2
}

E. XTUOJ 1172 因子和

数论函数


  • 利用因子和函数 σ ( a ) = ∏ i = 1 k p i e i + 1 − 1 p i − 1 \sigma(a) = \prod_{i = 1}^{k}\dfrac{p_i^{e_i + 1} - 1}{p_i - 1} σ(a)=i=1kpi1piei+11,其中 p 1 , p 2 , … , p k p_1, p_2, \dots, p_k p1,p2,,pk e 1 , e 2 , … , e k e_1, e_2, \dots ,e_k e1,e2,,ek 来自于整数的唯一分解: N = p 1 e 1 p 2 e 2 … p k e k N = p_1^{e_1}p_2^{e_2} \dots p_k^{e_k} N=p1e1p2e2pkek 中的质因数及其指数
  • 函数中的 p e i + 1 p^{e_i + 1} pei+1 可以轻易地由唯一分解中得到,省去求幂

核心代码

void E_1008_solve () {
    ans = 1;
    for (int i = 2; i * i <= n; ++i) {
        int tmp = 1;
        while (n % i == 0) {
	        n /= i;
	        tmp *= i;
        }
        ans = ans * (tmp * i - 1) / (i - 1);
    }
    if (n > 1) ans *= (n + 1);
}

F. XTUOJ 1452 完全平方数 I

分解质因数


  • a + ( a + 1 ) + ⋯ + a + n − 1 = ( a + a + n − 1 ) n 2 = n ( a + n − 1 2 ) a + (a + 1) + \dots + a + n - 1 = \dfrac{(a + a + n - 1) n}{2} = n(a + \dfrac{n - 1}{2}) a+(a+1)++a+n1=2(a+a+n1)n=n(a+2n1)
  • 易知上式是 n n n 的整数倍,若它为完全平方数,由 n n n 的唯一分解 n = ∏ i = 1 k p i e i n = \prod_{i = 1}^{k}p_i^{e_i} n=i=1kpiei,要使 n n n 的倍数为完全平方数,只需要把唯一分解中的所有奇指数配成偶数即可
  • 若最后得到的结果小于 n − 1 2 \dfrac{n-1}{2} 2n1,需要在此基础上再乘上一个完全平方数

核心代码

void F_1008_solve () {
    ans = n == 1 ? 0 : 1;
    int tmp = (n - 1) / 2;
    for (int i = 2, e; i * i <= n; ++i) {
        if (n % i == 0) {
            for (e = 0; n % i == 0; ++e) n /= i;
            if (e & 1) ans *= i;
        }
    }
    if (n > 1) ans *= n;
    for (int i = 2; ans < tmp; ++i) {  // 结果小于 (n + 1) / 2,需要继续乘上一个完全平方数
        if (ans * i * i >= tmp) { 
            ans = ans * i * i;
            break; 
        }
    }
    ans -= tmp;
}

G. XTUOJ 1560 完全平方数 II

分解质因数


  • a x 2 + b x + c = k 2 ax^2 + bx + c = k^2 ax2+bx+c=k2,由于 x ∈ Z + x \in Z^+ xZ+,解方程得 x = − b + b 2 − 4 c + 4 k 2 2 x = \dfrac{-b + \sqrt{b^2 - 4c + 4k^2}}{2} x=2b+b24c+4k2 ,易知当 b 2 − 4 c = 0 b^2 - 4c = 0 b24c=0 时,方程有无穷多个解,此外,设 b 2 − 4 c + 4 k 2 = t 2 b^2 -4c + 4k^2 = t^2 b24c+4k2=t2,因式分解得到 ( t − 2 k ) ( t + 2 k ) = b 2 − 4 c (t - 2k)(t + 2k) = b^2 - 4c (t2k)(t+2k)=b24c
  • 由上一步得到的最终表达式,可以求出 b 2 − 4 c b^2 - 4c b24c 的所有因子对,如果某对因子和能整除 2 2 2,可以认为该因子和是 2 t 2t 2t,由此可以求得 ∣ t ∣ \left \vert t \right \vert t,再验证一下 − b + ∣ t ∣ -b + \left \vert t \right \vert b+t 是否大于 0 0 0 以及能否被 2 2 2 整除,若都可以就可以确定一个解

核心代码

int G_1008_solve () {
    memset(ans, 0, sizeof(ans));
    int n = b * b - 4 * c, cntOfAns = -1;
    if (n == 0) return cntOfAns;
    int nAbs = n > 0 ? n : -n;
    cntOfAns = 0;
    for (int i = 1, tmp; i * i <= nAbs; ++i) {
        if (nAbs % i == 0) {
            tmp = n > 0 ? nAbs / i + i : nAbs / i - i; // 求得 |2t|
            if (tmp & 1 == 0) {                        // 验证求得的 |2t| 是否真地能被 2 整除
                tmp /= 2;
                tmp -= b;
            }
            else continue;
            if (tmp > 0 && tmp & 1 == 0) ans[cntOfAns++] = tmp / 2;
        }
    }
    return cntOfAns;
}

H. XTUOJ 1576 分段

分解质因数


  • A A A 的所有元素求和得到 S A S_A SA,最终的结果必然是 S A S_A SA 的因子
  • 我们可以从小到大依次枚举 S A S_A SA 的所有因子,第一个符合条件的就是答案

核心代码

void H_1008_solve () {
	// S[i] 表示 A[0] 到 A[i] 之和
    ans = S[n - 1];   // 设置 ans 初始值为 S[A - 1](A 中所有元素的和)
    int cnt = 0;
    for (int i = 1; i * i <= S[n - 1]; ++i) if (S[n - 1] % i == 0) A[cnt++] = i;
    if (cnt == 0) {
        ans = 0;
        return ;
    }
    for (int i = cnt - 1; i > 0; --i) A[cnt++] = S[n - 1] / A[i];
    // 枚举和的 S[A - 1] 的所有因子
    for (int i = 0; i < cnt; ++i) {
        int tmp = 1;
        for (int j = 0; j < n; ++j) {
            if (S[j] % A[i] == 0) {
                if (S[j] / A[i] == tmp) ++tmp;
                else goto next;
            }
        }
        if (A[i] < ans) ans = A[i];
        next: ;
    }
}  
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值