题目链接:http://acm.ustc.edu.cn/ustcoj/problem.php?id=1239;
题目大意是要求计算一个与p有关Sp,然后模p后取逆即结果。
其中p是4k+1型质数p > 10。
这题我们需要以下知识,原理。(主要是二次剩余)
1,-1是4k+1型质数的二次剩余。(利用欧拉判别法(-1)^ ((p - 1)/ 2)= 1, 所以是二次剩余), 所以如果i是二次剩余,p - i
也是二次剩余。
2, 利用拓展欧几里得算法求逆。
3,1^2 + 2^2 + ......+n^2 = (2n + 1)(n + 1)n / 6.
我们记 ri 为 i * i (mod p)的值
由于高斯函数性质[i*i / p] = i * i / p - ri / p;
所以Sp = (1^2 + 2^2 + ......+((p - 1) / 2)^2) / p - (r1 + r2 + ...... + r(p - 1)/ 2) / p.
(我们把i 与 p - i 配对,和为p, 有(p - 1) / 4 对)
= ((p - 1)/ 2) * ((p + 1)/ 2) * p / 6 - ((p - 1)/ 4)* p / p
= p * (p – 1) / 24 + (p - 1)/ 4
= (p - 1)(p - 5)/ 24;
所以我们已经求得Sp的值,可以看出Sp (mod p) = 5 / 24;此处 5 / 24为连分数 .则Sp关于p的逆为 24 / 5.我们求5 的逆再乘以24即结果。于是只需要调用一次eEuclid(5, p),得倒5的系数。但是这个欧几里得算法只给出一个满足5 * x + p * y = gcd(5,p) = 1的解不保证在p的最小非负完系中,所以最后乘以24后再处理一下即可。
Euclid算法中,我用一个结构体保存结果,注意用long long类型避免过程中两个需要的系数超范围。其他数据类型用int更适合计算机运行。
代码如下:
#include<stdio.h>
struct Euclid
{
int d;
long long x, y;
};
Euclid eEuclid(int p, int q)
{
Euclid eu;
if (q == 0)
{
eu.d = p;
eu.x = 1;
eu.y = 0;
return eu;
}
else
{
eu.d = eEuclid(q, p % q).d;
eu.x = eEuclid(q, p % q).y;
eu.y = eEuclid(q, p % q).x - (p / q) * eEuclid(q, p % q).y;
return eu;
}
}
int main()
{
int k = 1, t, re, p;
scanf("%d", &t);
while (t--)
{
scanf("%d", &p);
re = 24 * eEuclid(5, p).x % p;
if (re < 0)
re += p;
printf("Case #%d: %d\n", k++, re);
}
return 0;
}
总结:
感觉这个代码并非最优,应当有更快的求模逆的算法,OJ上也看到了0ms通过的大牛, 这个代码用了8ms,还是需要不断学习完善自己。加油!