您的位置:首页 > 其它

URAL 1132 二次剩余

2015-11-12 20:12 337 查看
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; }
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: