zoukankan      html  css  js  c++  java
  • 高次剩余学习笔记 / P5668

    考前学算法,就是这么浪。


    (x^aequiv bpmod p)([0,p)) 内的所有解。

    核心思想是通过原根将幂变成指数。但是普通的模数 (p)​​ 不一定有原根啊,那考虑使用分解质因数 (p=prod p_i^{alpha_i})​​,奇质数的幂是有原根的。拆开的理论依据是什么?换个元,令 (y=x^a)​​​,那么 (yequiv bpmod p)​​ 可以由若干个 (yequiv bpmod{p_i^{alpha_i}})​​ 联立而成的方程组等价表达,根据的是模数两两互质的 ExCRT(其实就是 CRT,可惜我不会写)。那么我们得到 (yequiv bpmod{p_i^{alpha_i}})​​ 的解集 (X_i)​​ 后,其实就是 (x)​​ 是原方程的解当且仅当(方程和方程组的等价性已经用 CRT 证明)对所有 (i)​​ 都有 (xin X_ipmod{p_i^{alpha_i}})​,于是我们可以从每个 (X_i)​ 各挑一个值出来用 CRT 合并,得到 (prod|X_i|)​ 个 ([0,p))​ 内的 (x)​ 便是原方程的所有解。这里使用了两次 CRT,正着一次反着一次,步步为等价变换。

    下面讨论 (x^aequiv bpmod{p^alpha}(pinmathbb P))​​​​​​​​ 怎么解。注意到当 (p eq 2)​​​​​​​​ 时 (p^{alpha})​​​​​​​​ 必有原根,我们分成 (poverset{?}=2)​​​​​​​​ 两种情况讨论,先考虑 (p eq 2)​​​​​​​​。首先如果 (b=0)​​​​​​​​,那么显然解集为 (p^{leftlceilfrac alpha a ight ceil}mid x)​​​​​​​​,下面 (b eq0)​​​​​​​​。若 (bperp p^{alpha})​​​​​​​​,求得 (p^{alpha})​​​​​​​​ 的任意一个原根(可以用 1/4 次 log 的算法求最小原根)(g)​​​​​​​​,然后用 BSGS 求出 (b=g^eta)​​​​​​​​ 的 (left[0,varphi=varphi!left(p^alpha ight) ight))​​​​​​​​ 内唯一 (eta)​​​​​​​​。此时显然 (x)​​​​​​​​ 是解仅当 (xperp p^alpha)​​​​​​​​,于是令 (x=g^y(yin[0,varphi)))​​​​​​​​,求出 (y)​​​​​​​​ 在 (modvarphi)​​​​​​​​ 意义下所有解,就相当于求出了 (x)​​​​​​​​ 的所有解。原方程即 (g^{ay}equiv g^etapmod{p^alpha})​​​​​​​​,跟据原根的幂周期为 (varphi)​​​​​​​​,这显然等价于 (ayequivetapmod{varphi})​​​​​​​​。线性同余方程就没什么讲的必要了吧,exgcd 找特解然后枚举通解在 ([0,varphi))​​​​​​​​ 内所有取值即可。

    如果 (b otperp p^alpha)​ 呢?此时 (b)​ 在模意义下没有原根。设 (b=p^eta q)​,令 (x=p^gamma r)​,那么原方程等价于 (p^{agamma}r^aequiv p^eta qpmod{p^alpha})​。此时我们发现,由于 (b eq 0)​,也就是 (eta<+infty)​ 即 (eta<alpha),那么 (p^{eta} q) 所在剩余系中都恰好包含 (eta) 个质因子 (p),那么我们需要 (agamma=eta)(并不是模意义下,而是严格相等!)!那么判一手 (amideta),直接得到 (gamma=dfraceta a),它现在是常数了。跟据同余式的性质将三边的 (p^eta) 约掉,得到 (r^aequiv qpmod{p^{alpha-eta}}),此时有 (p mid q),于是归约到了 (bperp p^alpha) 的情况。求出 (rmod p^{alpha-eta}) 的所有可能值后,对于其中每一个,我们要找出对应的所有 (left[0,p^alpha ight)) 内的 (p^gamma r),那么找到所有 (rinleft[0,p^{alpha-gamma} ight)) 即可,随便遍历一波,有 (p^{(alpha-gamma)-(alpha-eta)}=p^{eta-gamma})​ 个值。

    最后我们来考虑 (p=2)​​​​​​​​ 的情形。由于没有原根,很可惜,这种情况只能暴力(有原根的情况下复杂度显然是 (|X|+mathrm O!left(sqrt {p^alpha} ight))​​​​​​​​,而 (|X|)​​​​​​​​ 上限是有保证的)。直接暴力枚举 ([0,2^alpha))​​​​​​​​ 内所有 (x)​​​​​​​​ 显然会 T,我们可以剪枝呀!考虑对 (alpha)​​​​​​​​ 进行迭代,假设 (x^aequiv bpmod{2^{alpha-1}})​​​​​​​​ 的解集已经算出为 (X_{alpha-1})​​​​​​​​,那么 (x)​​​​​​​​ 是 (x^aequiv bpmod{2^{alpha}})​​​​​​​​ 的解的必要条件是满足 (alpha-1)​​​​​​​​ 的方程,即 (xin X_{alpha-1}pmod{2^{alpha-1}})​​​​​​​​,而对于每个 (x_0in X_{alpha-1})​​​​​​​​,在 (mod 2^alpha)​​​​​​​​ 意义下有两个对应值,都快速幂判一下即可。这样最坏复杂度依然是跑满的,但是剪枝效果很好(可以通过模板题),毕竟 (2^eta)​​​​​​​​ 时减掉一个枝相当于直接砍掉 (2^{alpha-eta})​​​​​​​​ 个可能的答案。

    code(写起来非常爽啊)
    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    #define pb push_back
    #define mp make_pair
    #define X first
    #define Y second
    int qpow(int x,int y,int p){
    	int res=1;
    	while(y){
    		if(y&1)res=res*x%p;
    		x=x*x%p;
    		y>>=1;
    	}
    	return res;
    }
    int exgcd(int a,int b,int &x,int &y){
    	if(!b)return x=1,y=0,a;
    	int d=exgcd(b,a%b,y,x);
    	return y-=a/b*x,d;
    }
    int inv(int a,int p){
    	int x,y,d=exgcd(a,p,x,y);
    	if(d==1)return 0;
    	return (x%p+p)%p;
    }
    int excrt(int a1,int a2,int p1,int p2){
    	int k1,k2,d=exgcd(p1,p2,k1,k2);
    	assert(d==1);
    	k1=(k1%(p1*p2)*(a2-a1)%(p1*p2)+p1*p2)%(p1*p2);
    	return (a1+p1*k1)%(p1*p2);
    }
    vector<int> solve2(int a,int b,int alpha){
    	b%=1<<alpha;
    	if(alpha==0)return vector<int>({0});
    	vector<int> v=solve2(a,b,alpha-1),ret;
    	for(int i=0;i<v.size();i++){
    		if(qpow(v[i],a,1<<alpha)==b)ret.pb(v[i]);
    		if(qpow(v[i]+(1<<alpha-1),a,1<<alpha)==b)ret.pb(v[i]+(1<<alpha-1));
    	}
    	return ret;
    }
    int proot(int p,int alpha,int palpha){
    	int phi=palpha/p*(p-1);
    	vector<int> dsr;
    	for(int i=2;i*i<=phi;i++)if(phi%i==0){
    		dsr.pb(i);
    		while(phi%i==0)phi/=i;
    	}
    	if(phi>1)dsr.pb(phi);
    	phi=palpha/p*(p-1);
    	for(int i=2;;i++)if(i%p){
    		bool flg=true;
    		for(int j=0;j<dsr.size();j++)flg&=qpow(i,phi/dsr[j],palpha)!=1;
    		if(flg)return i;
    	}
    }
    int bsgs(int g,int b,int p){
    	unordered_map<int,int> mmp;
    	int B=sqrt(p)+1,now=1,now0=1;
    	for(int i=1;i<=B;i++)now=now*g%p,mmp[now*b%p]=i;
    	for(int i=1;i<=B;i++){
    		now0=now0*now%p;
    		int j=mmp[now0];
    		if(j)return i*B-j;
    	}
    }
    vector<int> solve(int a,int b,int p,int alpha){
    	int palpha=qpow(p,alpha,1e12);
    //	cout<<palpha<<"!!
    ";
    	b%=palpha;
    	if(p==2)return solve2(a,b,alpha);
    	if(b==0){
    		int div=(alpha+a-1)/a,fst=qpow(p,div,1e12);
    		vector<int> ret;
    		for(int i=0;i<palpha;i+=fst)ret.pb(i);
    		return ret;
    	}
    	if(b%p==0){
    		int beta=0,q=b;
    		while(q%p==0)beta++,q/=p;
    		if(beta%a)return vector<int>();
    		vector<int> v=solve(a,q,p,alpha-beta);
    		int gamma=beta/a;
    		vector<int> ret;
    		int lim=qpow(p,beta-gamma,1e12),mul=qpow(p,alpha-beta,1e12),pgamma=qpow(p,gamma,1e12);
    		for(int i=0;i<v.size();i++)for(int j=0;j<lim;j++)ret.pb((v[i]+j*mul)%palpha*pgamma%palpha);
    		return ret;
    	}
    	int g=proot(p,alpha,palpha),beta=bsgs(g,b,palpha),phi=palpha/p*(p-1);
    	int x,y,d=exgcd(a,phi,y,x);
    	if(beta%d)return vector<int>();
    	int mod=phi/d,gap=qpow(g,mod,palpha);y=(y*(beta/d)%mod+mod)%mod;
    	int now=qpow(g,y,palpha);
    	vector<int> ret;
    	for(int i=0;y+i*mod<phi;i++)ret.pb(now),now=now*gap%palpha;
    	return ret;
    }
    void mian(){
    	int a,p,b;
    	cin>>a>>p>>b;
    //	cerr<<a<<" "<<b<<" "<<p<<":
    ";
    	vector<pair<int,int> > dsr;
    	for(int i=2;i*i<=p;i++)if(p%i==0){
    		int cnt=0;
    		while(p%i==0)cnt++,p/=i;
    		dsr.pb(mp(i,cnt));
    	}
    	if(p>1)dsr.pb(mp(p,1));
    	vector<int> ans;ans.pb(0);
    	int now=1;
    	for(int i=0;i<dsr.size();i++){
    		vector<int> v=solve(a,b,dsr[i].X,dsr[i].Y),nw;
    		for(int j=0;j<ans.size();j++)for(int k=0;k<v.size();k++)nw.pb(excrt(ans[j],v[k],now,qpow(dsr[i].X,dsr[i].Y,1e12)));
    		ans.swap(nw),now*=qpow(dsr[i].X,dsr[i].Y,1e12);
    	}
    	sort(ans.begin(),ans.end());
    	cout<<ans.size()<<"
    ";
    	for(int i=0;i<ans.size();i++)printf("%lld%c",ans[i]," 
    "[i+1==ans.size()]);
    }
    signed main(){
    //	freopen("P5668_1.in","r",stdin);freopen("out.out","w",stdout);
    	int t;
    	cin>>t;
    	while(t--)mian();
    	return 0;
    }
    
    珍爱生命,远离抄袭!
  • 相关阅读:
    Codeforces 1316B String Modification
    Codeforces 1305C Kuroni and Impossible Calculation
    Codeforces 1305B Kuroni and Simple Strings
    Codeforces 1321D Navigation System
    Codeforces 1321C Remove Adjacent
    Codeforces 1321B Journey Planning
    Operating systems Chapter 6
    Operating systems Chapter 5
    Abandoned country HDU
    Computer HDU
  • 原文地址:https://www.cnblogs.com/ycx-akioi/p/solution-p5668.html
Copyright © 2011-2022 走看看