bzoj2219 数论之神 欧拉降幂+BSGS+原根

题目分析

题目要求 x A ≡ B ( m o d P ) x^A \equiv B \pmod{P} xAB(modP)的解的个数。

首先将 P P P分解质因数,对于每个方程 x A ≡ b i ( m o d p i a i ) x^A \equiv b_i \pmod{p_i^{a_i}} xAbi(modpiai),求出解的个数。假设我们确定了这个方程的解,最后用中国剩余定理合并,不同方程组肯定对应不同的解,所以将每个方程的解数乘起来就是最终解的个数。

对于单个方程,分三种情况讨论。

情况1: b i = 0 b_i=0 bi=0

说明若将 x x x写成 p t q p^tq ptq的形式, t t t因子的大小至少是 ⌈ a A ⌉ \lceil \frac{a}{A} \rceil Aa,那么 q q q的大小就可以取 [ 1 , p a − ( ⌈ a A ⌉ ) ] [1,p^{a-(\lceil \frac{a}{A} \rceil)}] [1,pa(Aa)],并且其中一些取值有 p p p因子所以也会包括 t t t取更大的值的情况,所以该情况下的解数是 p a − ( ⌈ a A ⌉ ) p^{a-(\lceil \frac{a}{A} \rceil)} pa(Aa)

情况2: p ∣ b p|b pb

b = p t d b=p^td b=ptd,则 x A ≡ p t d ( m o d p a ) x^A \equiv p^td \pmod{p^a} xAptd(modpa)。若 A ̸ ∣ t A \not| t A̸t,则无解,否则

( x p t A ) A ≡ d ( m o d p a − t ) (\frac{x}{p^{\frac{t}{A}}})^A \equiv d \pmod{p^{a-t}} (pAtx)Ad(modpat)

就转化成情况3了。

但是在情况2中,本来 x p t A \frac{x}{p^{\frac{t}{A}}} pAtx的取值范围是 [ 0 , p a − t A ) [0,p^{a-\frac{t}{A}}) [0,paAt)的,所以解数还要乘一个 p t − t A p^{t-\frac{t}{A}} ptAt

情况3: p ̸ ∣ b p \not| b p̸b

求出 p a p^a pa的原根 g g g p a p^a pa不是质数啊,剩余系大小是 ϕ ( p a ) \phi(p^a) ϕ(pa)啊QAQ)

那么让 g r ≡ b ( m o d p a ) g^r \equiv b \pmod{p^a} grb(modpa) r r r用BSGS求出,则根据欧拉降幂得出下面的式子,原 x x x的解数是下面这个式子的解数:

A x ≡ r ( m o d p a   m o d   ϕ ( p a ) ) Ax \equiv r \pmod{p^a \bmod{\phi(p^a)}} Axr(modpamodϕ(pa))

后面那一串记作 p ′ p' p吧。

那么这个式子,若 g c d ( p ′ , A ) ̸ ∣ r gcd(p',A) \not| r gcd(p,A)̸r,无解,否则根据同余方程通解的表现形式(解出的任意 x x x x + t p ′ g c d ( p ′ , A ) x+t\frac{p'}{gcd(p',A)} x+tgcd(p,A)p),解数为 g c d ( p ′ , A ) gcd(p',A) gcd(p,A)

代码

#include<bits/stdc++.h>
using namespace std;
#define RI register int
int T,A,B,P,kP,ans;
int pri[100005];

int gcd(int x,int y) {return y?gcd(y,x%y):x;}
int fast_pow(int x,int y) {
	int re=1;
	for(;y;y>>=1,x=x*x) if(y&1) re=re*x;
	return re;
}
int fast_pow(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;
}
map<int,int> mp;
int bsgs(int x,int y,int p) {
	if(y==1) return 0;
	mp.clear();int m=ceil(sqrt((double)p)),q=1;
	for(RI i=0;i<m;++i) mp[(int)(1LL*q*y%p)]=i+1,q=1LL*q*x%p;
	for(RI i=m,x=1;;i+=m) {
		x=1LL*x*q%p;
		if(mp[x]) return i-mp[x]+1;
		if(i>p) return -1;
	}
}
int getg(int x,int phi) {
	int js=0,kphi=phi;
	for(RI i=2;i*i<=kphi;++i)
		if(kphi%i==0) {pri[++js]=i;while(kphi%i==0) kphi/=i;}
	if(kphi!=1) pri[++js]=kphi;
	for(RI i=2;;++i) {
		int flag=1;
		if(fast_pow(i,phi,x)!=1) continue;
		for(RI j=1;j<=js;++j)
			if(fast_pow(i,phi/pri[j],x)==1) {flag=0;break;}
		if(flag) return i;
	}
}
int getans(int p,int a,int kB) {
	if(kB==0) return fast_pow(p,a-((a-1)/A+1));
	int t=0;while(kB%p==0) kB/=p,++t;
	if(t%A) return 0;
	a-=t;int pa=fast_pow(p,a);
	int phi=pa-pa/p,g=getg(pa,phi),r=bsgs(g,kB,pa),d=gcd(phi,A);
	if(r%d) return 0;
	return d*fast_pow(p,t-t/A);
}

int main()
{
	scanf("%d",&T);
	while(T--) {
		scanf("%d%d%d",&A,&B,&P);
		P=P+P+1,kP=P,ans=1;
		for(RI i=2;i*i<=kP;++i) if(kP%i==0) {
			int js=0,pa=1;
			while(kP%i==0) kP/=i,++js,pa*=i;
			ans*=getans(i,js,B%pa);
		}
		if(kP!=1) ans*=getans(kP,1,B%kP);
		printf("%d\n",ans);
	}
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值