1 条题解

  • 0
    @ 2025-8-24 22:11:18

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Kewth
    ???

    搬运于2025-08-24 22:11:18,当前版本为作者最后更新于2019-10-21 21:40:17,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    blog

    二次剩余,俗称模意义开根
    也就是对于常数 nn 解这样一个方程:

    x2n(modp)x^2 \equiv n \pmod{p}

    这里只介绍模数 pp 为奇素数的解法,也就是 Cipolla 算法。

    以下运算皆指模 pp 意义下的运算。

    解的数量

    严格来讲,非 0 数 nn 是二次剩余当且仅当方程 x2nx^2 \equiv n 有解,也就是能开根。
    上述方程无解的非 0 数 nn 称作非二次剩余。

    对于二次剩余 nnx2nx^2 \equiv n 有多少解?

    假设有多组解,对于任意两个不相等的解 x0,x1x_0, x_1 ,有 x02x12x_0^2 \equiv x_1^2
    移项后平方差,得到 (x0x1)(x0+x1)0(x_0 - x_1)(x_0 + x_1) \equiv 0

    由于 pp 是奇素数,且 x0x1x_0 \ne x_1x0x1x_0 - x_1 在模 pp 意义下是不会为 0 的。
    故有 x0+x10x_0 + x_1 \equiv 0 ,也就是说两个不相等的解一定是相反数,
    换言之,该方程只有两个解,且它们互为相反数。
    而当 pp 为奇素数时模意义的两个相反数不会相等,因为奇偶性不同。

    还可以知道,任意一对相反数都对应一个二次剩余,而且这些二次剩余是两两不同的。
    也就说二次剩余的数量恰为 p12\frac{p-1}{2} ,其他的非 0 数都是非二次剩余,数量也是 p12\frac{p-1}{2}

    欧拉准则

    如何快速判断一个数 nn 是否为二次剩余?

    以下讨论假定 n 不为 0 。

    观察费马小定理 np11n^{p-1} \equiv 1 ,由于 pp 是奇素数,可以得到 n2(p12)10n^{2(\frac{p-1}{2})} - 1\equiv 0
    也就是说 np12n^{\frac{p-1}{2}} 是 1 开根的结果,根据上面所说, 1 开根只有两个解 1 和 -1 。
    那么 np12n^{\frac{p-1}{2}} 只能是 1 或 -1 。

    nn 是二次剩余,则有 $n^{\frac{p-1}{2}} \equiv (x^2)^{\frac{p-1}{2}} \equiv x^{p-1} \equiv 1$ 。

    np121n^{\frac{p-1}{2}} \equiv 1 ,将 nn 表示为 gkg^k , 其中 gg 是模 pp 意义下的原根。
    那么有 gkp121g^{k\frac{p-1}{2}} \equiv 1 由于 gg 是原根,必有 p1kp12p-1|k\frac{p-1}{2}
    也就是说 kk 一定是偶数,那么令 xgk2x \equiv g^{\frac{k}{2}} 即是 nn 开根的结果,这说明 nn 是二次剩余。

    也就是说 np121n^{\frac{p-1}{2}} \equiv 1nn 是二次剩余是等价的,
    由于 np12n^{\frac{p-1}{2}} 不为 1 是只能是 -1 ,那么 np121n^{\frac{p-1}{2}} \equiv -1nn 是非二次剩余等价。

    ps: 网上一堆伪证(包括楼下一篇题解)说若 nn 是非二次剩余,不存在 xx 使得上式为 1 ,但这只能说明上式为 -1 时 nn 是非二次剩余,并不能推翻“当 nn 是非二次剩余时上式为 1”

    Cipolla

    对于二次剩余解方程 x2nx^2 \equiv n

    找到一个 aa 满足 a2na^2 - n 是非二次剩余,由于非二次剩余的数量接近 p2\frac{p}{2}
    通过随机 + 检验的方式期望约 2 次可以找到这样一个 aa

    接下来定义 i2a2ni^2 \equiv a^2 - n
    但是 a2na^2 - n 不是二次剩余,怎么找得到这样一个 ii

    类比实数域到复数域的推广,定义这样一个 ii ,然后可以将所有数表示为 A+BiA+Bi 的形式,
    其中 A,BA, B 都是模 pp 意义下的数,类似于实部和虚部。

    那么 (a+i)p+1n(a + i)^{p+1} \equiv n ,考虑证明。

    引理 1ipii^p \equiv -i

    证明: $i^p \equiv i(i^2)^{\frac{p-1}{2}} \equiv i(a^2 - n)^{\frac{p-1}{2}} \equiv -i $

    引理 2(A+B)pAp+Bp(A + B)^p \equiv A^p + B^p

    证明:二项式定理展开后,由于 pp 是质数,除了 Cp0,CppC_p^0, C_p^p 外的组合数分子上的阶乘没法消掉,模 pp 都会为 0 ,剩下来的就是 Cp0A0Bp+CppApB0C_p^0 A^0 B^p + C_p^p A^p B^0

    现在证明上述结论:

    $$(a + i)^{p+1} \equiv (a^p + i^p) (a + i) \equiv (a - i) (a + i) \equiv a^2 - i^2 \equiv n $$

    那么 (a+i)p+12(a + i)^{\frac{p+1}{2}} 即是一个解,其相反数是另一个解。

    然而还剩最后一个问题, (a+i)p+12(a + i)^{\frac{p+1}{2}} 的“虚部”一定为 0 吗?

    幸运的是,的确如此,假设存在 (A+Bi)2n(A + Bi)^2 \equiv nB0B \ne 0
    那么有 A2+B2i2+2ABinA^2 + B^2i^2 + 2ABi \equiv n ,即 A2+B2(a2n)n2ABiA^2 + B^2(a^2 - n) - n \equiv -2ABi
    式子的左边“虚部”为 0 ,那么式子右边的虚部也一定为 0 ,也就是说 AB0AB \equiv 0
    既然假设了 B0B \ne 0 那么一定是 A0A \equiv 0 ,也就是说 (Bi)2n(Bi)^2 \equiv n
    也就是 i2nB2i^2 \equiv nB^{-2} ,由于 B2B^2 是个二次剩余,其逆元 B2B^{-2} 一定也是二次剩余,乘上二次剩余 nn 后一定还是二次剩余,这与 i2i^2 是个非二次剩余产生矛盾。

    实现

    实现的时候弄个“复数”类(据说也可以不用)即可。

    参考实现:

    #include <cstdio>
    #include <cstdlib>
    typedef long long lolong;
    
    int mod;
    lolong I_mul_I; // 虚数单位的平方
    
    struct complex {
    	lolong real, imag;
    	complex(lolong real = 0, lolong imag = 0): real(real), imag(imag) { }
    };
    inline bool operator == (complex x, complex y) {
    	return x.real == y.real and x.imag == y.imag;
    }
    inline complex operator * (complex x, complex y) {
    	return complex((x.real * y.real + I_mul_I * x.imag % mod * y.imag) % mod,
    			(x.imag * y.real + x.real * y.imag) % mod);
    }
    
    complex power(complex x, int k) {
    	complex res = 1;
    	while(k) {
    		if(k & 1) res = res * x;
    		x = x * x;
    		k >>= 1;
    	}
    	return res;
    }
    
    bool check_if_residue(int x) {
    	return power(x, (mod - 1) >> 1) == 1;
    }
    
    void solve(int n, int p, int &x0, int &x1) {
    	mod = p;
    
    	lolong a = rand() % mod;
    	while(!a or check_if_residue((a * a + mod - n) % mod))
    		a = rand() % mod;
    	I_mul_I = (a * a + mod - n) % mod;
    
    	x0 = int(power(complex(a, 1), (mod + 1) >> 1).real);
    	x1 = mod - x0;
    }
    
    • 1

    信息

    ID
    4490
    时间
    1000ms
    内存
    125MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者