[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} (y^m + 1)((x-y)^m + 1) \]

接下来将式子拆开,我们发现 \(\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\) 也是同理,故我们要求:

\[\sum\limits_{y=1}^{p-1} y^m (x-y)^m \]

注意这里 \(y\) 的下界变成了 \(1\),是因为 \(y=0\) 时该式必然为 \(0\)。

将后面的式子使用二项式定理展开,得到:

\[\sum\limits_{y=1}^{p-1} y^m \sum\limits_{i=0}^{m} x^i (-y)^{m-i} \dbinom{m}{i} \]

交换求和顺序:

\[\sum\limits_{i=0}^{m} x^i (-1)^{m-i} \dbinom{m}{i} \sum\limits_{y=1}^{p-1} y^{2m-i} \]

注意到 \(y^{2m} = y^{p-1} = 1\),故答案可以化作:

\[\sum\limits_{i=0}^{m} x^i (-1)^{m-i} \dbinom{m}{i} \sum\limits_{y=1}^{p-1} y^{-i} \]

此时再一次将 \(x^i\) 扔进去并交换求和顺序,得到:

\[\sum\limits_{y=1}^{p-1} \sum\limits_{i=0}^{m} (\dfrac{x}{y})^i (-1)^{m-i} \dbinom{m}{i} \]

再次使用二项式定理,可以得到:

\[\sum\limits_{y=1}^{p-1} (\dfrac{x}{y} - 1)^m \]

考虑分类讨论。当 \(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;
}
posted @ 2026-05-26 16:27  Oken喵~  阅读(38)  评论(0)    收藏  举报