题解:P5668 【模板】N 次剩余
Upd:用递推解模
此题解将使用复杂度正确的方法解决模
解关于整数
首先按照惯例,分解
回想
同样的方法可以应用到此题。于是分情况:
首先是
然后当
接下来是
最后是
下面是详细证明。
引理 1(中国剩余定理)
证明:显然映射
f 成立,考虑证明f 映射到的两两不同。使用反证法,假设x_1\ne x_2 ,同时x_1\bmod p_1^{q_1}=x_2\bmod p_1^{q_1},x_1\bmod p_2^{q_2}=x_2\bmod p_2^{q_2},\dots 。可以发现x_1-x_2 必然是所有p_i^{q_i} 的倍数,又因为p_i^{q_i} 都是两两互素,所以得到x_1-x_2 是m 的倍数,矛盾。证毕。
于是可以分解原方程为若干互相独立的方程,并逐个求解,最后合并。
引理 2
证明:
充分性:则须证明对于所有
x ,(x^n)^{\frac{\varphi(m)}{\gcd(n,\varphi(m))}}\equiv1\pmod m 。显然可化为x^{\operatorname{lcm}(n,\varphi(m))}\equiv1\pmod m ,由欧拉定理可证。必要性:设
m 的一个原根为g ,显然存在唯一k'\in\Z_{\varphi(m)} 满足g^{k'}\equiv k\pmod m ,亦存在唯一x'\in\Z_{\varphi(m)} 满足g^{x'}\equiv x\pmod m 。则条件变为k'\cdot\frac{\varphi(m)}{\gcd(n,\varphi(m))}\equiv0\pmod{\varphi(m)} ,可得k' 必然是\gcd(n,\varphi(m)) 的倍数。另一方面,要证明原方程有解,则要证明x'n\equiv k'\pmod{\varphi(m)} 有解,而此方程有解的条件即为k' 是\gcd(n,\varphi(m)) 的倍数(根据 Bézout's Lemma)。得证。
然后就可以判别对于一个
引理 3
证明:使用归纳法。显然
q=3 时成立。假设q=i 成立,有5^{2^{i-3}}\equiv2^{i-1}+1\pmod{2^i} ,则5^{2^{i-3}}\equiv2^{i-1}+1\pmod{2^{i+1}} 或者5^{2^{i-3}}\equiv2^i+2^{i-1}+1\pmod{2^{i+1}} 。两侧同时平方,前一个得到5^{2^{i-2}}\equiv2^{2i-2}+2^i+1\equiv2^i+1\pmod{2^{i+1}} ,后一个得到5^{2^{i-2}}\equiv2^{2i+1}+2^{2i-2}+2^{i+1}+2^i+1\equiv2^i+1\pmod{2^{i+1}} 。于是q=i+1 成立。根据归纳,证毕。
引理 4
证明:易知
5 的阶必定是\varphi(2^q)=2^{q-1} 的因数,也就是2^i(i\in\Z_q) 。而如果5^{2^i}\equiv1\pmod{2^q} ,则5^{2^j}\equiv1\pmod{2^q}(j\ge i) ,于是根据引理 3,5 的阶必然>2^{q-3} 。另一方面,引理 3 两侧同时平方,有5^{2^{q-2}}\equiv2^{2q-2}+2^q+1\equiv1\pmod{2^q} ,所以5 的阶必然\le2^{q-2} 。综上有5 的阶为2^{q-2} ,证毕。
引理 5
证明:首先,根据引理 4,
5 的阶是2^{q-2} ,所以\{5^0\bmod2^q,5^1\bmod2^q,\dots,5^{2^{q-2}-1}\bmod2^q\} 中的元素互不相同。证明没有二解,使用反证法,假设(-1)^{a_1}5^{b_1}\equiv(-1)^{a_2}5^{b_2}\pmod{2^q} ,由上得a_1\ne a_2 。于是有5^{b_1}+5^{b_2}\equiv0\pmod{2^q} ,但是左侧模4 为2 ,右侧模4 为0 ,矛盾。从而得证。证明不可能无解,则由上有所有(-1)^a5^b 互不相同,而一共有2^{q-1} 个(-1)^a5^b ,于是一定可以和所有x 对应,证毕。
发现
至此完整地给出了
先解决
引理 6
证明:反证。若
x 含p 少于\lceil\frac qn\rceil ,则x^n 含p 少于\lceil\frac qn\rceil\cdot n ,也就少于q ,矛盾。证毕。
然后是不互素。
引理 7
证明:直接构造。设
x^n\equiv u\pmod{p^{q-t}} 的解为x_0 ,则原方程解为(x_0+z\cdot p^{q-t})\cdot p^{\frac tn} 。证毕。
至此全部情况梳理完毕。时间复杂度为
代码用了下 pb_ds 的哈希表(用来加速 BSGS)。处理
#include<stdio.h>
#include<vector>
#include<ext/pb_ds/assoc_container.hpp>
typedef long long ll;
bool f;
int lp,le,e[2000001];
ll n,m,k,p[21],pw[21],c[21];
std::vector<ll>r[21];
__gnu_pbds::gp_hash_table<ll,ll>h;
ll gcd(ll x,ll y){return y?gcd(y,x%y):x;}
ll Pow(ll a,ll b,ll m)
{
ll s=1;
for(a%=m;b;a=a*a%m,b>>=1)
if(b&1)
s=s*a%m;
return s;
}
ll BSGS(ll a,ll b,ll m)
{
a%=m,b%=m,h.clear();
if(b==1)
return 0;
if(!a&&b)
return -1;
ll s=sqrt(m)+1,aa=1;
for(ll i=0;i<s;i++)
h[b*aa%m]=i,aa=aa*a%m;
for(ll i=1,j=aa;i<=s;i++,j=j*aa%m)
{
int x=h[j];
if(x||j==b)
return i*s-x;
}
return -1;
}
ll exgcd(ll a,ll b,ll&x,ll&y)
{
if(!b)
return x=1,y=0,a;
int g=exgcd(b,a%b,y,x);
return y-=a/b*x,g;
}
bool DiscreteRoots(ll z)
{
if(k%pw[z])
{
ll kp=k%pw[z],pk=1,mk=0;
while(kp%p[z]==0)
kp/=p[z],pk*=p[z],mk++;
if(mk%n)
return 1;
ll pwk=pw[z]/pk,h=pwk/p[z]*(p[z]-1),g,le=0,e[21];
if(Pow(kp,h/gcd(n,h),pwk)!=1)
return 1;
for(ll i=2;i*i<=h;i++)
if(h%i==0)
{
while(h%i==0)
h/=i;
e[le++]=i;
}
if(h>1)
e[le++]=h;
for(g=1;;g++)
{
bool f=1;
for(int i=0;i<le&&f;i++)
if(Pow(g,pwk/p[z]*(p[z]-1)/e[i],pwk)==1)
f=0;
if(f)
break;
}
ll x=BSGS(g,kp,pwk),ka,kb,y=exgcd(n,pwk/p[z]*(p[z]-1),ka,kb);
if(x%y)
return 1;
h=pwk/p[z]*(p[z]-1)/y,kb=Pow(p[z],mk/n,pw[z]),r[z].resize(y*(pk/kb)),r[z][0]=Pow(g,(ka*(x/y)%h+h)%h,pwk),ka=kb,kb=Pow(g,h,pwk);
for(ll i=1;i<y;i++)
r[z][i]=r[z][i-1]*kb%pwk;
for(ll i=y;i<y*(pk/ka);i++)
r[z][i]=(r[z][i-y]+pwk)%(pw[z]/ka);
for(ll i=0;i<y*(pk/ka);i++)
r[z][i]=r[z][i]*ka;
}
else
{
ll x=0,y=1,o=Pow(p[z],n,pw[z]);
while(y)
x++,y=y*o%pw[z];
r[z].resize(o=pw[z]/(y=Pow(p[z],x,pw[z]+1)));
for(ll i=0;i<o;i++)
r[z][i]=i*y;
}
return 0;
}
void dfs(ll st,ll x)
{
if(st==lp)
{
e[le++]=x;
return;
}
for(ll i:r[st])
dfs(st+1,(x+i*c[st])%m);
return;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%lld%lld%lld",&n,&m,&k),f=1,lp=le=0;
if(n==1)
{
printf("1\n%lld\n",k);
continue;
}
if(!(m&7))
{
ll mp=3;
p[0]=2,pw[0]=8,m>>=3;
while(!(m&1))
m>>=1,pw[0]<<=1,mp++;
if(k&pw[0]-1)
{
ll k2=k&pw[0]-1,r2=0;
while(!(k2&1))
k2>>=1,r2++;
if(r2%n)
{
puts("0");
continue;
}
if(pw[0]>>r2>4)
{
ll w_1=0,w5=BSGS(5,k2,pw[0]>>r2);
if(!~w5)
w_1=1,w5=BSGS(5,(pw[0]>>r2)-k2,pw[0]>>r2);
if(n&1)
{
ll x,y,g=exgcd(n,pw[0]>>r2+2,x,y);
if(w5%g)
{
puts("0");
continue;
}
w5/=g,x=x*w5&(pw[0]>>r2+2)/g-1,y=Pow(5,(pw[0]>>r2+2)/g,pw[0]>>r2),r[0].resize(g),r[0][0]=w_1?(pw[0]>>r2)-Pow(5,x,pw[0]>>r2):Pow(5,x,pw[0]>>r2);
for(ll i=1;i<g;i++)
r[0][i]=r[0][i-1]*y&(pw[0]>>r2)-1;
}
else if(w_1)
{
puts("0");
continue;
}
else
{
ll x,y,g=exgcd(n,pw[0]>>r2+2,x,y);
if(w5%g)
{
puts("0");
continue;
}
w5/=g,x=x*w5&(pw[0]>>r2+2)/g-1,y=Pow(5,(pw[0]>>r2+2)/g,pw[0]>>r2),r[0].resize(g<<1),r[0][g]=(pw[0]>>r2)-(r[0][0]=Pow(5,x,pw[0]>>r2));
for(ll i=1;i<g;i++)
r[0][i]=r[0][i-1]*y&(pw[0]>>r2)-1,r[0][i+g]=r[0][i+g-1]*y&(pw[0]>>r2)-1;
}
}
else
for(ll i=0;i<pw[0]>>r2;i++)
if(Pow(i,n,pw[0]>>r2)==k&(pw[0]>>r2)-1)
r[0].emplace_back(i);
ll s=r[0].size();
r[0].resize(s<<r2-r2/n);
for(ll i=s;i<s<<r2-r2/n;i++)
r[0][i]=r[0][i-s]+(pw[0]>>r2)&(pw[0]>>r2/n)-1;
for(ll i=0;i<s<<r2-r2/n;i++)
r[0][i]=r[0][i]<<r2/n;
}
else
{
ll l=1ll<<mp-(mp-1)/n-1;
r[0].resize(l);
for(ll i=0;i<l;i++)
r[0][i]=i<<(mp-1)/n+1;
}
lp++;
}
else if(!(m&3))
{
p[0]=2,pw[0]=4,m>>=2;
for(ll i=0;i<4;i++)
if(Pow(i,n,4)==(k&3))
r[0].emplace_back(i);
if(r[0].empty())
{
puts("0");
continue;
}
lp++;
}
else if(!(m&1))
p[0]=2,pw[0]=2,m>>=1,r[0].emplace_back(k&1),lp++;
for(ll i=3;i*i<=m;i+=2)
if(m%i==0)
{
p[lp]=i,pw[lp]=1;
while(m%i==0)
m/=i,pw[lp]*=i;
if(DiscreteRoots(lp))
{
f=0;
break;
}
lp++;
}
if(!f)
{
puts("0");
while(lp--)
r[lp].clear();
continue;
}
if(m>1)
{
p[lp]=pw[lp]=m,m=1;
if(DiscreteRoots(lp))
{
puts("0");
while(lp--)
r[lp].clear();
continue;
}
lp++;
}
for(ll i=0;i<lp;i++)
m*=pw[i];
for(ll i=0;i<lp;i++)
c[i]=m/pw[i]*Pow(m/pw[i],pw[i]/p[i]*(p[i]-1)-1,pw[i])%m;
dfs(0,0),std::sort(e,e+le),printf("%lld\n",le);
for(ll i=0;i<le;++i,i<le?putchar(' '):putchar('\n'))
printf("%lld",e[i]);
while(lp--)
r[lp].clear();
}
return 0;
}