• 二次剩余学习笔记


    学习: https://kewth.blog.luogu.org/solution-p5491

    定义

    对于p,n,若存在x,满足(x^2 equiv n pmod{p}),则称n为模p意义下的二次剩余,即n在模p意义下能开方,计算二次剩余就是计算x,x在模p意义下和(sqrt{n})等价

    下文仅对p为奇素数时进行讨论

    判定

    如何判断n是否是模p意义下的二次剩余?

    p为奇素数,有如下结论:

    (1)a是模p的二次剩余的充要条件是(a^{(p-1)/2} equiv 1 pmod{p})

    (2)a是模p的非二次剩余的充要条件是(a^{(p-1)/2} equiv -1 pmod{p})

    (3)当a时模p的二次剩余时,同余方程有两个解

    性质

    二次剩余有如下特性:

    (1) 若a1,a2都是模p的二次剩余,a1*a2也是模p的二次剩余

    (2) 若a1,a2都是模p的非二次剩余,a1*a2是模p的二次剩余

    (3) 若a1是模p的二次剩余,a2是模p的非二次剩余,a1*a2是模p的非二次剩余

    (4)(n^2 equiv (p−n)^2 pmod{p})

    (5) p的二次剩余和二次非剩余的个数均为(p−1)/2

    求解方法

    首先判断这个数数n是否是二次剩余,如果n是0则答案只有一个,就是0

    若n不为0,且有二次剩余,则进行如下运算:

    随机一个数a,使得(w=a^2-n)为非二次剩余,

    然后((a+i)^{(p+1)/2})就是一个解,另一个解是它在模p意义下的相反数

    代码

    /*洛谷P5491
    若有解,则按模p后递增的顺序输出在模p意义下的全部解.
    若两解相同,只输出其中一个,
    若无解,则输出Hola!
    */
    #include <cstdio>
    #include <cstdlib>
    typedef long long ll;
    
    int mod;
    ll I_mul_I; // 虚数单位的平方
    
    struct complex {
        ll real, imag;
        complex(ll real = 0, ll 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) {//判断x是否是模p意义下的二次剩余
        return power(x, (mod - 1) >> 1) == 1;
    }
    
    void solve(int n, int p, int &x0, int &x1) {
        mod = p;
    
        ll 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;
    }
    int main (){
        int T;
        scanf("%d",&T);
        while(T--){
            int n,p,x0,x1;
            scanf("%d%d",&n,&p);
            mod = p;
            if(n==0){//若n为0,有一个解0
                printf("0
    ");
            }
            else if(check_if_residue(n)){//n不为0且有解,则必有一对不同解
                solve(n,p,x0,x1);
                if(x0<x1){
                    printf("%d %d
    ",x0,x1);
                }
                else{
                    printf("%d %d
    ",x1,x0);
                }
            }
            else{
                printf("Hola!
    ");
            }
        }
    }
    
  • 相关阅读:
    记录一下
    Fiddler对谷歌浏览器抓包
    Linux环境部署基本步骤
    JS----this && JS继承
    节流与防抖
    JS---call apply bind的区别&&JS---argument
    浏览器输入url之后到最后网页渲染出来经历了什么
    Bom中的方法
    JS----new和object.create的区别
    有关排序
  • 原文地址:https://www.cnblogs.com/ucprer/p/13360401.html
Copyright © 2020-2023  润新知