URAL1132_Square Root

求解方程,x^2=n (mod P)。

解二次同余方程的步骤:

1、首先判断勒让德符号(n,p)是否的等于1,即n^((p-1/2)=1 (mod p)是否成立。不成立显然无解。(略)

2、任取0-(p-1)中的一a值,判断w=a*a-n是否是P的二次同余,直到找到一个否定的答案即可。(大约有一半是否定答案)

3、根据找到的w,(a+sqrt(w))^((p+1)/2)就是二次同余的解,同时最多只有两解,且两数之和为P。(要用到二次域,囧rz)

中间有一定量的推导过程,但是不是很难,琢磨琢磨吧。

 

对于这个题目,注意一种特殊情况,p=2时,直接输出1即可。

 

 

召唤代码君:

 

 

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

int a,w;
int T,n,p,A0,B0;

struct twice{
    int A,B;
    twice() { }
    twice(int AA,int BB) { A=AA,B=BB; }
    twice operator * ( twice T ) const {
        A0=A*T.A+(B*T.B)%p*w;
        B0=A*T.B+B*T.A;
        return twice(A0%p,B0%p);
    }
};

int power(int A,int B)
{
    int C=1;
    while (B){
        if (B&1) C=C*A%p;
        A=A*A%p,B>>=1;
    }
    return C;
}

twice power(twice A,int B)
{
    twice C(1,0);
    while (B){
        if (B&1) C=C*A;
        A=A*A,B>>=1;
    }
    return C;
}

bool lgd(int A,int B)
{
    return power(A,(B-1)/2)==1;
}

int main()
{
    scanf("%d",&T);
    while (T--){
        scanf("%d%d",&n,&p);
        if (p==2){
            puts("1");
            continue;
        }
        if (!lgd(n,p)){
            puts("No root");
            continue;
        }
        for (;;){
            a=rand()%p;
            w=((a*a-n)%p+p)%p;
            if (!lgd(w,p)) break;
        }
        twice T(a,1);
        T=power(T,(p+1)/2);
        int ans1=(int)T.A,ans2=(int)p-T.A;
        if (ans1>ans2) swap(ans1,ans2);
        printf("%d %d\n",ans1,ans2);
    }
    return 0;
}

 

posted @ 2014-08-02 13:21  092000  阅读(441)  评论(0编辑  收藏  举报