URAL 1132 二次剩余

URAL 1132
题目链接:
http://acm.timus.ru/problem.aspx?space=1&num=1132
题意:
对于方程x * x = n (mod p)是否有解,p是素数且与n互素
思路:
二次剩余版题。
感谢ACdreamer大神http://blog.csdn.net/acdreamers/article/details/10182281
关于二次剩余:
概念:x * x = n(mod p),存在解时,x为n关于p的二次剩余(p是奇素数)
相关定理:
1)n ^ ((p - 1) / 2) = +-1mod(p)
a)费马小定理知,然后用完全平方差因式分解
2)方程有解当且仅当n ^ (p-1) / 2 = 1mod(p)
a)
3)对于a >= 0 && a < p,若w = a * a - n满足w关于p没有二次剩余,则(a+sqrt(w))^((1+p)/2)为方程的一个解。
4)方程至多两个解,且满足x1 + x2 = p.
算法步骤:
1)判断x * x = n (mod p)是否有解
2)枚举a求出合法的w
3)(难理解的地方)由于方程(a+sqrt(w))^((1+p)/2)出现根号,所以要用类似复数乘法的二元域计算乘法,详见代码。
4)二元域的第一项为解,求出另一个解。
本题有坑点就是a不一定小于n,所以a>n时直接特判n==2 && a==1会WA。
源码:
乱版:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <string>
using namespace std;
#define LL long long
struct Twice
{
    LL t1, t2;
    LL i, mod;
    Twice(){t1 = t2 = i = 0;}
    Twice(LL _t1, LL _t2, LL _t3, LL p){t1 = _t1, t2 = _t2, i = _t3, mod = p;}
    Twice operator * (Twice a)const{
        Twice ans;
        ans.t1 = (t1 * a.t1 % a.mod + t2 * a.t2 % a.mod * a.i) % a.mod;
        ans.t2 = (t1 * a.t2 % a.mod + t2 * a.t1 % a.mod) % a.mod;
        ans.i = i;
        ans.mod = mod;
        return ans;
    }
};
Twice power(Twice a, LL x, LL p)
{
    Twice ans = Twice(1, 0, a.i, p);
    while(x){
        if(x & 1)   ans = (ans * a);
        a = (a * a);
        x >>= 1;
    }
    return ans;
}
LL ppow(LL a, LL x, LL p)
{
    LL res = 1;
    while(x){
        if(x & 1)   res = (res * a) % p;
        a = (a * a) % p;
        x >>= 1;
    }
    return res;
}
LL Legendre(LL a, LL p)
{
    return ppow(a, (p - 1) >> 1, p);
}
LL solve(LL a, LL p)
{
    if(p == 2)  return 1;
    if(Legendre(a, p) + 1 == p)    return -1;
    LL t1, t2;
    for(LL i = 0 ; i < p ; i++){
        LL temp = i * i - a;
        temp = (temp % p + p) % p;
        if(Legendre(temp, p) + 1 == p){
            t1 = i, t2 = temp;
            break;
        }
    }
// printf("t1 = %I64d, t2 = %I64d\n", t1, t2);
    Twice u = Twice(t1, 1, t2, p);
    u = power(u, (p + 1) / 2, p);
    return u.t1;
}
int main()
{
    int T;
    LL a, n;
    scanf("%d", &T);
    while(T--){
        cin >> a >> n;
        a %= n;
// scanf("%lld%lld", &a, &n);
        LL res = solve(a, n);
        if(n == 2){
            if(a == 1)  printf("1\n");
            continue;
        }
        if(res == -1){
            printf("No root\n");
            continue;
        }
// printf("res = %I64d\n", res);
        LL b = n - res;
        if(res > b)   swap(res, b);
        if(res != b)  cout << res << " " << b << endl;
        else    cout << res << endl;
    }
    return 0;
}

精简版:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <string> using namespace std;
#define LL long long LL n, p; LL a, w; struct Twice {
    LL a, b;
    Twice(){}
    Twice(LL _a, LL _b){a = _a, b = _b;}
    Twice operator * (Twice rbs)const{
        Twice ans;
        ans.a = (a * rbs.a + b * rbs.b * w) % p;
        ans.b = (b * rbs.a + a * rbs.b) % p;
        return ans;
    } }; LL ppow(LL a, LL x) {
    LL res = 1;
    while(x){
        if(x & 1)   res = (res * a) % p;
        a = (a * a) % p;
        x >>= 1;
    }
    return res; } Twice ppow(Twice a, LL x) {
    Twice res = Twice(1, 0);
    while(x){
        if(x & 1)   res = (res * a);
        a = (a * a);
        x >>= 1;
    }
    return res; } LL Legendre(LL a, LL x) {
    return ppow(a, (x - 1) / 2); } LL solve(LL n, LL p) {
    if(Legendre(n, p) + 1 == p)    return -1;
    for(a = 0 ; a < p ; a++){
        w = a * a - n;
        w = (w % p + p) % p;
        if(Legendre(w, p) + 1 == p) break;
    }
    Twice ans = ppow(Twice(a, 1), (p + 1) / 2);
    return ans.a; } int main() {
    int T;
    scanf("%d", &T);
    while(T--){
        scanf("%I64d%I64d", &n, &p); // printf("n = %I64d, p = %I64d\n", n, p);
        if(p == 2){
            printf("1\n");
            continue;
        }
        LL a = solve(n, p);
        if(a == -1){
            printf("No root\n");
            continue;
        }
        LL b = p - a;
        if(a > b)   swap(a, b);
        printf("%I64d %I64d\n", a, b);
    }
    return 0; }

你可能感兴趣的:(URAL 1132 二次剩余)