[Code+#7] 同余方程
[Code+#7] 同余方程
来自 ini 大神的超级厉害做法喵。
首先我们考虑 \(p\) 为奇质数的情况,容易想到本题需要使用二次剩余。
我们定义 \(f(x,p) = x^{\frac{p-1}{2}}\),根据经典结论,我们知道 \(f(x,p) \in \{-1,0,1\}\),其中 \(-1\) 代表 \(x\) 不是 \(p\) 的二次剩余,\(0\) 代表 \(x=0\),对应的二次剩余解只有 \(1\) 个,\(1\) 代表 \(x\) 是 \(p\) 的二次剩余,且对应的解有 \(2\) 个。
也就是说,令 \(a^2 = y\),\(m = \dfrac{p-1}{2}\),我们可以得到答案的表示:
接下来将式子拆开,我们发现 \(\sum\limits_{y=0}^{p-1} 1\) 必然为 \(p\)。对于 \(\sum\limits_{y=0}^{p-1} y^m\),通过二次剩余的统计形式来看,其必然等于 \(p-p=0\),\(\sum\limits_{y=0}^{p-1} (x-y)^m\) 也是同理,故我们要求:
注意这里 \(y\) 的下界变成了 \(1\),是因为 \(y=0\) 时该式必然为 \(0\)。
将后面的式子使用二项式定理展开,得到:
交换求和顺序:
注意到 \(y^{2m} = y^{p-1} = 1\),故答案可以化作:
此时再一次将 \(x^i\) 扔进去并交换求和顺序,得到:
再次使用二项式定理,可以得到:
考虑分类讨论。当 \(x = 0\) 时,该式为 \((p-1) \times (-1)^m\),否则考虑由于 \(y\) 遍历了 \(1\) 到 \(p-1\),则 \(y\) 的逆元也会遍历 \(1\) 到 \(p-1\),此时乘以 \(x\) 后仍然能够保持便利的性质。也就是说原式可以看作 \(\sum\limits_{y=0}^{p-2} y^m\)。由于 \(\sum\limits_{y=0}^{p-1} y^m = 0\),故答案为 \(- (p-1)^m\),即 \((-1)^{m+1}\),于是我们解决了 \(p\) 为奇质数的情况。
考虑 \(p\) 不是奇质数,求出 \(p = P_1 \times P_2 \times \dots\),其中 \(P\) 为互不相同的奇质数。根据 CRT,我们求出所有 \(P_i\) 下的答案,相乘即可,解决了这道难题。
//Ad astra per aspera
#include<iostream>
#include<cstdio>
using namespace std;
const int N=10000000;
bool del[N+10];
int min_prime[N+10],prime[N+10],tot;
void init(){
for(int i=2;i<=N;i++){
if(!del[i]){
min_prime[i]=i;
prime[++tot]=i;
}
for(int j=1;j<=tot && i*prime[j]<=N;j++){
min_prime[i*prime[j]]=prime[j];
del[i*prime[j]]=true;
if(i%prime[j]==0){
break;
}
}
}
}
long long calc(int p,int x){
int m=(p-1)/2;
if(x==0){
long long ans=p;
if(m%2==0){
ans+=p-1;
}
else{
ans-=p-1;
}
return ans;
}
else{
long long ans=p;
if(m%2==1){
ans++;
}
else{
ans--;
}
return ans;
}
}
int main(){
init();
int T;
scanf("%d",&T);
while(T--){
int p,x;
scanf("%d %d",&p,&x);
int tmp=p;
long long ans=1;
while(tmp!=1){
int pre_p=min_prime[tmp],pre_x=x%min_prime[tmp];
ans*=calc(pre_p,pre_x);
tmp/=min_prime[tmp];
}
printf("%lld\n",ans);
}
return 0;
}

浙公网安备 33010602011771号