class="markdown_views prism-atelier-sulphurpool-light">
引言
我们在研究多项式开根的时候,想到了一个问题,若常数项不为1,那么怎么开根呢?
这就涉及到二次剩余。
那么什么是二次剩余呢?若在模
p意义下,存在一个
x,使得
x2≡a(modp),则称
a为
x关于
p的二次剩余。若不存在这样的
x,则称
a为非二次剩余。
现在我们知道了
a,要求解
x,怎么办呢?
p为奇素数
首先引入
欧拉准则:
当
a为模
p意义下的二次剩余时,
a2p−1≡1(modp)
当
a为模
p意义下的非二次剩余时,
a2p−1≡p−1(modp)
我们要求的
x满足:
(a−1x2)20≡1(modp)
设
p−1=s2t,设
xt−1=a2s+1,那么根据欧拉准则第一条,有:
(a−1xt−12)2t−1≡1(modp)
诶…要不我们设
(a−1xt−k2)2t−k≡1(modp),这样我们要求的就是
x0
思考如何从
xt−k推到
xt−k−1。
设
et−k=a−1xt−k2
若
et−k2t−k−1≡1(modp)(即将
et−k2t−k开根),显然
xt−k−1=xt−k
若
et−k2t−k−1≡p−1(modp)。我们试图让
xt−k乘一个值使得它变成
xt−k−1。那么想到欧拉准则的第二条,首先随机一个非二次剩余
b,然后发现
(b2k−1s)2t−k≡p−1(modp),所以
b2k−1s是我们满足条件的值,此时让
xt−k−1=b2k−1sxt−k即可。
最后
x0其实有正负两个解,都满足条件。
例题:URAL 1132
代码:
#include
using namespace std;
#define RI register int
int read() {
int q=0;char ch=' ';
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
return q;
}
int T,a,p,bin[22];
int ksm(int x,int y,int p) {
int re=1;
for(;y;y>>=1,x=1LL*x*x%p) if(y&1) re=1LL*re*x%p;
return re;
}