URAL 1132 Square Root <二次剩余 + 数论>

传送门:http://acm.timus.ru/problem.aspx?space=1&num=1132

题意:给你在1~p之间取x使得满足x^2 n(%p),若存在,输出x的值,否则输出No root

分析:

今天要讨论的问题是解方程,其中是奇质数。

 

引理:

 

证明:由费马小定理,

 

引理:方程有解当且仅当

 

定理:满足不是模的二次剩余,即无解,那么是二次

     剩余方程的解。

 

证明:,前面的等号用二项式定理和,后面的等

     号用了费马小定理和是模的二次非剩余。然后

 

      

 

在算法实现的时候,对的选择可以随机,因为大约有一半数是模的二次非剩余,然后快速幂即可。

 

************************************************************************************

升级:

接下来我们来解另一个二次同余方程的解,其中,并且是奇质数。方法如下

 

先求出方程的一个解,那么进一步有

 

      

 

我们知道

 

      

 

那么也就是说

 

       

 

可以证明,那么最终得到

 

       

 

这里由于不是素数,所以求逆元用扩展欧几里得算法即可。

 

 

例如:求方程的解

 

分析:利用上述方法求得,最终解得

 


code:

#include <iostream>
#include <cstdio>
using namespace std;

#define LL long long

LL quick_mod(LL a,LL b,LL p)//快速幂
{
    LL ans = 1;
    a%=p;
    while(b)
    {
        if(b&1)
        {
            ans = ans * a%p;
            b--;
        }
        b>>=1;
        a = a*a%p;
    }
    return (ans+p)%p;
}
LL Legendre(LL a,LL p)//求勒让得符号(-1,0,1)这里-1返回p-1
{
    return quick_mod(a,(p-1)>>1,p);
}
struct T //二次域
{
    LL p,d;
};
LL w; //二次域第二个单位参数

LL mod(LL t, LL p)
{
    t %=p;
    if(t<0) t+=p;
    return t;
}
T multi_er(T a,T b, LL p)//二次域乘法
{
    T ans;
    ans.p = (a.p*b.p%p+ a.d*b.d%p*w%p)%p;
    ans.d = (a.p*b.d%p +a.d*b.p%p)%p;
    return ans;
}
T pow_er(T a,LL b,LL p)//二次域上的快速幂
{
     T ans;
     ans.p=1;
     ans.d =0;

    while(b)
    {
        if(b&1)
        {
           ans = multi_er(ans, a, p);
            b--;
        }
        b>>=1;
        a = multi_er(a, a, p);
    }
    return ans;
}
int solve(int n,int p)
{
    if(p==2) return 1;
    if(Legendre(n,p)+1 == p) return -1;
   // printf("solve ...\n");
    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 = pow_er(tmp,(p+1)>>1,p);
    return ans.p;
}
int main()
{
   int cas;
   scanf("%d",&cas);
   while(cas--)
   {
       int n,p;
       scanf("%d %d",&n,&p);
       n %=p;
       int a = solve(n,p);
       if(a == -1)
       {
           puts("No root");
           continue;
       }
       else
       {
           int b = p - a;
           if(a==b)
           {
               printf("%d\n",a);
           }
           else
           {
               if(a>b) swap(a,b);
               printf("%d %d\n",a,b);
           }
       }
   }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值