二次剩余

参考博客1:
参考博客2
参考博客3

二次剩余形式: x 2 ≡ n   m o d   p x^2\equiv n\ mod\ p x2n mod p,给定n,p,求解x?
这里我们只谈论p为奇素数。p为2时,答案不是0就是1,这点在下面代码有体现(代码是照搬的)。
简明扼要:首先先判断勒让德符号(Legendre symbol) ( a p ) = a p − 1 2 (\frac{a}{p})=a^{\frac{p-1}{2}} (pa)=a2p1
此时有三种情况:
( n p ) = { 1 , a 在 模 p 是 二 次 剩 余 − 1 , a 在 模 p 不 是 二 次 剩 余 0 , a = 0 (\frac{n}{p})=\left\{\begin{matrix} 1,a在模p是二次剩余\\ -1,a在模p不是二次剩余\\ 0,a=0 \end{matrix}\right. (pn)=1,ap1,ap0,a=0

有解的条件为: ( n p ) = n p − 1 2 = 1 (\frac{n}{p})=n^{\frac{p-1}{2}}=1 (pn)=n2p1=1
那么我们先找到一个a,满足 w = a 2 − n w=a^2-n w=a2n不是模p的二次剩余,即 x 2 ≡ w   m o d   p x^2\equiv w\ mod\ p x2w mod p无解,那么 x ≡ ( a + w ) p + 1 2 x\equiv(a+w)^{\frac{p+1}{2}} x(a+w)2p+1即为解,且这解有两个数,它们互为相反数。

代码:

#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;
typedef long long LL;

LL quick_mod(LL a, LL b, LL m)
{
    LL ans = 1;
    a %= m;
    while(b)
    {
        if(b & 1)
        {
            ans = ans * a % m;
            b--;
        }
        b >>= 1;
        a = a * a % m;
    }
    return ans;
}

struct T
{
    LL p, d;
};

LL w;

//二次域乘法
T multi_er(T a, T b, LL m)
{
    T ans;
    ans.p = (a.p * b.p % m + a.d * b.d % m * w % m) % m;
    ans.d = (a.p * b.d % m + a.d * b.p % m) % m;
    return ans;
}

//二次域上快速幂
T power(T a, LL b, LL m)
{
    T ans;
    ans.p = 1;
    ans.d = 0;
    while(b)
    {
        if(b & 1)
        {
            ans = multi_er(ans, a, m);
            b--;
        }
        b >>= 1;
        a = multi_er(a, a, m);
    }
    return ans;
}

//求勒让德符号
LL Legendre(LL a, LL p)
{
    return quick_mod(a, (p-1)>>1, p);
}

LL mod(LL a, LL m)
{
    a %= m;
    if(a < 0) a += m;
    return a;
}

LL Solve(LL n,LL p)
{
    if(n==0) return 0;
    if(p == 2) return 1;
    if (Legendre(n, p) + 1 == p)
        return -1;
    LL a = -1, t;
    while(true)
    {
        a = rand() % p;
        t = a * a - n;
        w = mod(t, p);
        if(Legendre(w, p) + 1 == p) break;
    }
    T tmp;
    tmp.p = a;
    tmp.d = 1;
    T ans = power(tmp, (p + 1)>>1, p);
    return ans.p;
}

int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        int n, p;
        scanf("%d %d",&n,&p);
        n %= p;
        int a = Solve(n, p);
        if(a == -1)
        {
            puts("No root");
            continue;
        }
        int b = p - a;
        if(a > b) swap(a, b);
        if(a == b)
            printf("%d\n",a);
        else
            printf("%d %d\n",a,b);
    }
    return 0;
}

应用:我们可以用二次剩余求解一元二次同余方程。
a x 2 + b x + c ≡ 0 ( m o d   p )    ( a ≠ 0 & & g c d ( a , q ) ≠ q & & g c d ( b , q ) ≠ q ) ax^2+bx+c\equiv0(mod\ p)\ \ (a\neq0 \&\&gcd(a,q)\neq q\&\&gcd(b,q)\neq q) ax2+bx+c0(mod p)  (a̸=0&&gcd(a,q)̸=q&&gcd(b,q)̸=q)
那么上式就等价为:
( 2 a x + b ) 2 ≡ ( b 2 − 4 a c ) ( m o d   p ) (2ax+b)^2\equiv(b^2-4ac)(mod\ p) (2ax+b)2(b24ac)(mod p)



题目:
题意:给出 b , c , p = 1 e 9 + 7 b,c,p=1e9+7 bcp=1e9+7,找出一对 x , y , 满 足 0 ≤ x ≤ y < p ( x + y ) m o d   p = b ( x ∗ y ) m o d   p = c x,y,满足0\leq x\leq y< p\\ (x+y)mod\ p=b\\(x*y)mod\ p=c x,y0xy<p(x+y)mod p=b(xy)mod p=c,如果找不到,输出-1,-1

题解:我们观察第一个式子,且x,y的取值范围,我们可以得到:
x = b − y ∣ ∣ x = b + p − y x=b-y||x=b+p-y x=byx=b+py,我们分别代入第二条式子,就是我们熟悉的二次剩余了,题目说了唯一解,那么我们只要找到一个就可以了。

#include

using namespace std;
typedef long long LL;

LL quick_mod(LL a, LL b, LL m)
{
    LL ans = 1;
    a %= m;
    while(b)
    {
        if(b & 1)
        {
            ans = ans * a % m;
            b--;
        }
        b >>= 1;
        a = a * a % m;
    }
    return ans;
}

struct T
{
    LL p, d;
};

LL w;

//二次域乘法
T multi_er(T a, T b, LL m)
{
    T ans;
    ans.p = (a.p * b.p % m + a.d * b.d % m * w % m) % m;
    ans.d = (a.p * b.d % m + a.d * b.p % m) % m;
    return ans;
}

//二次域上快速幂
T power(T a, LL b, LL m)
{
    T ans;
    ans.p = 1;
    ans.d = 0;
    while(b)
    {
        if(b & 1)
        {
            ans = multi_er(ans, a, m);
            b--;
        }
        b >>= 1;
        a = multi_er(a, a, m);
    }
    return ans;
}

//求勒让德符号
LL Legendre(LL a, LL p)
{
    return quick_mod(a, (p-1)>>1, p);
}

LL mod(LL a, LL m)
{
    a %= m;
    if(a < 0) a += m;
    return a;
}

LL Solve(LL n,LL p)
{
//    printf("n=%lld p=%lld\n",n,p);
        if(n==0) return 0;
    if(p == 2) return 1;
    if (Legendre(n, p) + 1 == p)
        return -1;
    LL a = -1, t;
    while(true)
    {
        a = rand() % p;
        t = a * a - n;
        w = mod(t, p);
        if(Legendre(w, p) + 1 == p) break;
    }
    T tmp;
    tmp.p = a;
    tmp.d = 1;
    T ans = power(tmp, (p + 1)>>1, p);
    return ans.p;
}

bool so(LL b,LL c,LL b_)
{
    LL p=1000000007;
//        int n, p;
//        scanf("%d %d",&n,&p);
//        n %= p;
        LL a = Solve(b*b-4*c, p);
        bool flag=1;
        if(a != -1)
        {
            LL y=(a-b)/(-2);
            LL x=b-y;
            if((x+y)%p==b_&&(x*y)%p==c){
                    flag=0;
                    if(x>y) swap(x,y);
                printf("%lld %lld\n",x,y);return 1;
            }
            y=(p-a-b)/-2;
            x=b-y;
            if((x+y)%p==b_&&(x*y)%p==c){
                flag=0;
                if(x>y) swap(x,y);
                printf("%lld %lld\n",x,y);return 1;
            }
//            puts("No root");
        }
        return 0;

}

int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        LL b,c;
        scanf("%lld%lld",&b,&c);LL b_=b;
        if(so(b,c,b_)) continue;

       b+=1000000007;
       if(so(b,c,b_)) continue;
       else puts("-1 -1");
    }
    return 0;
}

你可能感兴趣的:(数论)