N次剩余
Determinant · · 题解
本文将介绍一种不求离散对数求任意模数
初步转化
根据维基百科,求任意模数二次剩余等价于对模数进行质因数分解。因此本题不弱于质因数分解,不妨先做个质因数分解,转化为求
这里为了避免一些问题,我们先将
(1)若
现在
(2)若
现在
(3)记
注记:不难注意到
现在
(4)令
注记:现在
现在
模质数的情形
模
不妨将
(5)当
(6)当
现在
这里的解决方法是把 实际上,本题数据中直接取 1 就能过。
现在
令
继续这样两边同时开根然后除过去的操作,最终
两边开根,
牛顿迭代
现在我们已经求出了模
正如经典的求模
定义
可以验证 p-adic 赋值满足如下三条性质:
(1)正定性:
(2)积性:
(3)强三角不等式:
对于解析函数
引理 若
证明 在
而
于是
定理 若
证明 在
由引理知
于是
本题中对
由二项式定理可知,
另外,对于
最终,我们得到了
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef __int128 lll;
mt19937 rnd(114514);
struct FactorList{
map<int,int>mp;
FactorList(){}
FactorList(int x){mp[x]=1;}
void operator +(const int &h){mp[h]++;}
void operator +=(const FactorList &h){
for(auto it:h.mp)mp[it.first]+=it.second;
};
void print(){int fl=0;for(auto it:mp){if(fl)putchar('*');fl=1;printf("%d^%d",it.first,it.second);}puts("");}
};
namespace Factorize{
const int N=1000;
int m,c[31],pr[3]={2,7,61};FactorList f[N];
void init(){
for(int i=2;i<N;++i){
int fl=1;
for(int j=2;j*j<=i;++j)if(i%j==0){fl=0;f[i]=f[i/j];f[i]+=j;break;}
if(fl)f[i]=i;
}
}
bool chk(int n,int x){
int v=pr[x],p=n;n--;m=1;while(!(n&1))n>>=1,++m;c[m]=1;
while(n){if(n&1)c[m]=1ll*c[m]*v%p;v=1ll*v*v%p;n>>=1;}
for(int i=m-1;i;--i)c[i]=1ll*c[i+1]*c[i+1]%p;
for(int i=1;i<=m;++i){
if(c[i]!=1&&c[i]!=p-1)return 0;
if(i==1&&c[i]==p-1)return 0;
if(i==m||c[i]==p-1)return 1;
}
}
bool chk(int n){
for(int i=0;i<3;++i)if(!chk(n,i))return 0;return 1;
}
int getd(int n){
int s=0,t=0,c=rnd()&((1<<30)-1);
while(1){
int val=1;for(int i=1;;i<<=1){
for(int j=1;j<=i;++j){
t=(1ll*t*t+c)%n;val=1ll*val*abs(t-s)%n;
if(!(i%31)){ll q=__gcd(n,val);if(q>1)return q;}
}
int q=__gcd(n,val);if(q==n)return getd(n);if(q>1)return q;s=t;
}
}
}
FactorList sol(int n){
if(n<N)return f[n];
if(chk(n))return n;
int d=getd(n);FactorList h=sol(d);h+=sol(n/d);return h;
}
};
void exgcd(int a,int b,int &x,int &y){
if(!b){x=1;y=0;return;}
exgcd(b,a%b,y,x);y-=a/b*x;
}
int inv(int x,int p){int a,b;if(x<0)x+=p;exgcd(x,p,a,b);if(a<0)a+=p;return a;}
ll fpowll(ll x,int y,ll p){
ll s=1;while(y){if(y&1)s=(__int128)s*x%p;x=(__int128)x*x%p;y>>=1;}return s;
}
int fpow(int x,int y,int p){
int s=1;while(y){if(y&1)s=1ll*s*x%p;x=1ll*x*x%p;y>>=1;}return s;
}
int fpow(int x,int y){
int s=1;while(y){if(y&1)s=s*x;x=x*x;y>>=1;}return s;
}
vector<int> enumerate(int l,int r,int d){
vector<int>v;for(int i=l;i<r;i+=d)v.push_back(i);
return v;
}
vector<int> getroot0(int n,int k,int p){
int t=0,d=p-1,w,u,npow[32]={1};while(d%n==0)d/=n,t++;for(int i=1;i<t;++i)npow[i]=npow[i-1]*n;
FactorList N=Factorize::sol(n);unordered_map<int,int>mp;
if(__gcd(n,d)>1){
vector<int>w0,w1={k},w2;
for(auto i:N.mp){
for(int j=1;j<=i.second;++j){
w2.clear();
for(int k:w1){
w0=getroot0(i.first,k,p);
for(int l:w0)w2.push_back(l);
}
swap(w1,w2);
}
}
return w1;
}
while(1){
w=fpow(u=rnd(),npow[t-1]*d,p);if(w<0)w+=p;if(w==0||w==1)continue;
int fl=1;for(auto it:N.mp)if(fpow(w,n/it.first,p)==1){fl=0;break;}if(fl)break;
}
for(int i=0,c=1;i<n;++i)mp[c]=i,c=1ll*c*w%p;u=fpow(u,d,p);
int ans=0,d_=max(inv(n,d),1),k_=fpow(k,d_*n-1,p);
for(int i=t-2;i>=0;--i){
int h=1ll*fpow(k_,npow[i],p)*fpow(u,ans,p)%p;
ans+=npow[t-1]*(n-mp[h]);ans/=n;
}
k=1ll*fpow(k,d_,p)*fpow(u,ans,p)%p;
vector<int>v;for(int i=0;i<n;++i)v.push_back(k),k=1ll*k*w%p;return v;
}
vector<int> getroot(int n,int k,int p){
if(!k)return {0};
if(!n){if(k==1)return enumerate(1,p,1);return {};}
if(n==1)return {k};
if(!k)return {0};n+=p-1;
int g=__gcd(n,p-1);
k=fpow(k,inv(n,p-1),p);n=g;if(n==1)return {k};
if(fpow(k,(p-1)/n,p)!=1)return {};
return getroot0(n,k,p);
}
vector<int> getroot(int n,int k,int p,int pn){
int q=fpow(p,pn),fac=1,fac1=1,fac2=q;k%=q;
if(!k)return enumerate(0,q,fpow(p,(pn+n-1)/n));
int h=0,u=p;while(k%p==0)k/=p,q/=p,h++;
if(h%n)return {};
fac=fpow(p,h/n);fac1=fpow(p,pn-h+h/n);pn-=h;
vector<int>v=getroot(n%(p-1),k%p,p);vector<int>V;
int n0=n;while(n%p==0)u*=p,n/=p,fac1/=p;u=min(u,q);fac1=max(fac1,fac*p);
ll q0=1ll*u/p;
for(int i:v)for(int j=i;j<u;j+=p){
int x=j,x1,fl=0;
for(int z=1;z<7;++z){
ll t0=fpowll(x,n0-1,q0*q),t=(lll)t0*x%(q0*q);
if((t-k)%q==0){fl=1;break;}
x1=((k-t)/q0*inv(t0%q*n%q,q)+x)%q;if((x1-x)%u)break;x=x1;
}
if(!fl)continue;
if(x<q)x+=q;x*=fac;x%=fac1;fl=0;for(int j=x;j<fac2;j+=fac1){V.push_back(j);if(j+x==fac2)fl=1;}
if(p==2&&!fl&&q0>1)for(int j=x;j<fac2;j+=fac1)V.push_back(fac2-j);
break;
}
return V;
}
bool chkroot(int n,int k,int p,int pn){
int h=0,u=p,q=fpow(p,pn),ord;k%=q;if(!k)return 1;while(k%p==0)k/=p,q/=p,h++;if(h%n)return 0;if(q==1)return 1;
ord=1ll*(p-1)*q/p;n%=ord;if(!n)return (k==1);
if(p==2&&q<=4){for(int i=0;i<q;++i)if(fpow(i,n,q)==k)return 1;return 0;}
if(p==2)ord>>=1;n=__gcd(n,ord);return (fpow(k,ord,p)==1);
}
void sol(){
int n,m,k;FactorList M;vector<int>w0,w1,w2;w1.push_back(0);
scanf("%d%d%d",&n,&m,&k);M=Factorize::sol(m);
for(auto it:M.mp)if(!chkroot(n,k,it.first,it.second)){puts("0");return;}
for(auto it:M.mp){
int h=fpow(it.first,it.second);h=(1ll*inv(m/h,h)*m/h)%m;
w0=getroot(n,k,it.first,it.second);
if(w0.empty()){w1={};break;}
w2.resize(w1.size()*w0.size());
int k=0;for(int i:w1)for(int j:w0)w2[k++]=((1ll*j*h+i)%m);
swap(w1,w2);
}
printf("%d\n",(int)w1.size());sort(w1.begin(),w1.end());for(int i=0;i<w1.size();++i){printf("%d",w1[i]);if(i<w1.size()-1)putchar(' ');else puts("");}
}
int main(){
Factorize::init();
int t;scanf("%d",&t);while(t--)sol();
return 0;
}
闲话
写这篇文章,可以说是为了一瓶醋包了一碗饺子。当时在搜 p-adic 牛顿迭代有没有什么理论依据,找到了 On a p-adic newton method,看了看分析还蛮有意思的,然后就想着能不能有什么应用,就搞出来了这个东西。
在这篇题解中,我们只用了原论文中结论的一个特例。我们只用到 p-adic 赋值的正定性、积性和强三角不等式,实际上这正是非阿基米德赋值的三条性质。于是它可以推广到一般非阿基米德赋值域上。例如,在有理分式域上,类似地,定义