用户工具

站点工具


2020-2021:teams:i_dont_know_png:potassium:math_theory_revision_1

差别

这里会显示出您选择的修订版和当前版本之间的差别。

到此差别页面的链接

两侧同时换到之前的修订记录 前一修订版
后一修订版
前一修订版
2020-2021:teams:i_dont_know_png:potassium:math_theory_revision_1 [2020/05/17 21:28]
potassium [解法2]
2020-2021:teams:i_dont_know_png:potassium:math_theory_revision_1 [2020/05/22 20:08] (当前版本)
potassium fix typos
行 11: 行 11:
 由欧拉定理,若 $(a,n)=1$ ,则 $a^{\varphi(m)}\equiv 1\pmod m$ 。 由欧拉定理,若 $(a,n)=1$ ,则 $a^{\varphi(m)}\equiv 1\pmod m$ 。
  
-类似单位复根,数 $m$ 的原根 $g\in[1,​m],​(g,​m)=1$ 满足 $\{g,g^2,...,​g^{\varphi(m)}\}$ 构成模 $m$ 的一个既约剩余系。易得,对于质数 $m$ ,这个既约剩余系的取值范围为 $[1,m-1]$ 。+类似单位复根,数 $m$ 的原根 $g\in[1,​m],​(g,​m)=1$ 满足 $\{g,g^2,\ldots,​g^{\varphi(m)}\}$ 构成模 $m$ 的一个既约剩余系。易得,对于质数 $m$ ,这个既约剩余系的取值范围为 $[1,m-1]$ 。
  
 原根的另一个定义是, $\forall p<​\varphi(m),​ g^{\varphi(m)}\neq 1$ ,即 $\varphi(m)$ 是最小的让 $g^d\equiv 1\pmod m$ 的正整数 $d$ 。 原根的另一个定义是, $\forall p<​\varphi(m),​ g^{\varphi(m)}\neq 1$ ,即 $\varphi(m)$ 是最小的让 $g^d\equiv 1\pmod m$ 的正整数 $d$ 。
行 45: 行 45:
 设 $x=iB+t$ (或者 $x=iB-t$ ,都可),其中 $B$ 为块大小,先设为 $\left\lceil\sqrt c\right\rceil$ 。 设 $x=iB+t$ (或者 $x=iB-t$ ,都可),其中 $B$ 为块大小,先设为 $\left\lceil\sqrt c\right\rceil$ 。
  
-那么有 $a^{iB}\equiv b\cdot a^{-t}$ ,把右半部分预处理并扔进 map 里,从小到大枚举 $i\in[0,B]$ ,通过扩欧解出来右半边 $a^{-t}$ 应当取的值,查map判断,即可 $\mathcal{O}(\sqrt c)$ 求解。+那么有 $a^{iB}\equiv b\cdot a^{-t}$ ,把右半部分预处理并扔进 map 里,从小到大枚举 $i\in[0,B]$ ,通过扩欧解出来右半边 $a^{-t}$ 应当取的值,查map判断,即可 $O(\sqrt c)$ 求解。
  
 ====== N次剩余 ====== ====== N次剩余 ======
行 51: 行 51:
 即: $x^n\equiv a\pmod m$ ,给定 $m\in prime,n,a$ ,求出 $x$ 。 即: $x^n\equiv a\pmod m$ ,给定 $m\in prime,n,a$ ,求出 $x$ 。
  
-前置芝士:扩欧,原根, ​$BSGS+前置芝士:扩欧,原根, BSGS 
  
 模板题:[[http://​acm.hdu.edu.cn/​showproblem.php?​pid=3930|HDU3930 Broot]](数据范围有误,应为 $1e12$ 。数据较弱,提供[[https://​paste.ubuntu.com/​p/​gC2QR6tFcq/​|几组稍强数据]]) 模板题:[[http://​acm.hdu.edu.cn/​showproblem.php?​pid=3930|HDU3930 Broot]](数据范围有误,应为 $1e12$ 。数据较弱,提供[[https://​paste.ubuntu.com/​p/​gC2QR6tFcq/​|几组稍强数据]])
行 59: 行 59:
 设 $m$ 的一个原根为 $g$ ,由于 $a,​x\in[0,​m-1]$ ,必有 $x=0$ 或正整数 $y$ 满足 $g^{y}=x$ , $a=0$ 或正整数 $z$ 满足 $g^{z}=a$ 。 设 $m$ 的一个原根为 $g$ ,由于 $a,​x\in[0,​m-1]$ ,必有 $x=0$ 或正整数 $y$ 满足 $g^{y}=x$ , $a=0$ 或正整数 $z$ 满足 $g^{z}=a$ 。
  
-容易通过 ​$BSGS求出 $z$ ,现在 $g^{yn}\equiv g^{z} \pmod m$ 式中只有 $y$ 未知。+容易通过 BSGS 求出 $z$ ,现在 $g^{yn}\equiv g^{z} \pmod m$ 式中只有 $y$ 未知。
  
 式子等价于: $yn\equiv z\pmod \varphi(m)$ ,可以用扩欧求出 $y$ 的一个解 $y_0$ 。 式子等价于: $yn\equiv z\pmod \varphi(m)$ ,可以用扩欧求出 $y$ 的一个解 $y_0$ 。
行 65: 行 65:
 设 $gcd=\gcd(n,​\varphi(m))$ , $y$ 的解集为 $\{k\in[0, gcd)|y_0+k\cdot \frac{\varphi(m)}{gcd}\}$ 。再根据此求出 $x$ 即可。 设 $gcd=\gcd(n,​\varphi(m))$ , $y$ 的解集为 $\{k\in[0, gcd)|y_0+k\cdot \frac{\varphi(m)}{gcd}\}$ 。再根据此求出 $x$ 即可。
  
 +
 +<hidden 解法1>
 +<​code:​c++>​
 +#​include<​cstdio>​
 +#​include<​algorithm>​
 +#​include<​queue>​
 +#​include<​map>​
 +#​include<​cstring>​
 +#​include<​cmath>​
 +#​include<​cstdlib>​
 +#​include<​set>​
 +#​include<​unordered_map>​
 +#​include<​vector>​
 +typedef long long ll;
 +using namespace std;
 +#define pb push_back
 +#define mp make_pair
 +#define fi first
 +#define se second
 +#define N 1238747
 +#define hash HASH
 +using namespace std;
 +typedef long long ll;
 +ll md(ll x,ll m){return x>​=m?​x-m:​x;​}
 +ll mul(ll a,ll b,ll m){
 +    ll ans=0;  ​
 +    a=(a%m+m)%m;​b=(b%m+m)%m;​
 +    for(;​b;​b>>​=1,​a=md(a+a,​m))if(b&​1)ans=md(ans+a,​m);​
 +    return ans; 
 +}
 +ll qp(ll a,ll p,ll mod){
 + ll ans=1;
 + for(;​p;​p>>​=1,​a=mul(a,​a,​mod))if(p&​1)ans=mul(ans,​a,​mod);​
 + return ans;
 +
 +
 +//​---------------BSGS----------------
 +struct Triple{
 +    ll x,y,z;
 +    Triple(ll a,ll b,ll c) :​x(a),​y(b),​z(c) {}
 +};
 +Triple exgcd(ll a,ll b){
 +    if(b==0) return Triple(1,​0,​a);​
 +    const Triple last=exgcd(b,​a%b);​
 +    return Triple(last.y,​last.x-a/​b*last.y,​last.z);​
 +}
 +int A,B,C;
 +set<​ll>​S;​
 +unordered_map<​ll,​int>​ma;​
 +ll bsgs(ll A,ll B,ll C){
 +    ll sqrtn=ceil(sqrt(C)),​base=1;​
 +    ma.clear();
 +    for(int i=0;​i<​sqrtn;​i++){
 +        if(base)ma[base]=i;​
 +        base=mul(base,​A,​C);​
 +    }
 +    ll i=0,​j=-1,​D=1;​
 +    S.clear();
 +    for(;​i<​sqrtn;​i++){
 +        Triple res=exgcd(D,​C);​
 +        const ll c=C/res.z;
 +        res.x=(mul(res.x,​(B/​res.z),​c)+c)%c;​
 +        if(ma.count(res.x)){
 +       ​ j=ma[res.x];​
 +       ​ return (i*sqrtn+j);​
 + }
 +        D=mul(D,​base,​C);​
 +    }
 +    return -1;
 +}
 +//​------------sieve----------------
 +#define M 1000000
 +#define P 1000000
 +int isnp[M],​pri[P],​cnt_prime;​
 +void sieve(){
 + int i,j;
 + isnp[0]=isnp[1]=1;​
 + for(i=2;​i<​M;​i++){
 + if(!isnp[i])pri[cnt_prime++]=i;​
 + for(j=0;​j<​cnt_prime&&​1LL*i*pri[j]<​M;​j++){
 + isnp[i*pri[j]]=1;​
 + if(i%pri[j]==0)break;​
 + }
 + }
 +}
 +//​-------------pri factor------------
 +vector<​pair<​ll,​int>>​pris;​
 +void get_pf(ll x){
 + int i;
 + pris.clear();​
 + for(i=0;​i<​cnt_prime&&​1LL*pri[i]*pri[i]<​=x;​i++){
 + if(x%pri[i]==0){
 + int count=0;
 + while(x%pri[i]==0)count++,​x/​=pri[i];​
 + pris.pb(mp(pri[i],​count));​
 + }
 + }
 + if(x>​1)pris.pb(mp(x,​1));​
 +}
 +//​-----------primitive root-------------
 +int jud_proot(int g,int index,ll d,ll p){
 + // optimized
 + int i;
 + for(i=0;​i<​pris.size();​i++)if(qp(g,​(p-1)/​pris[i].first,​p)==1)return 0;
 + return 1;
 +
 + // origin
 + /​*if(index==pris.size())return !(qp(g,​d,​p)==1&&​d!=p-1);​
 + ll tmp=1;int i;
 + ll pri=pris[index].fi,​count=pris[index].se;​
 + for(i=0;​i<​=count;​i++){
 + if(!jud_proot(g,​index+1,​d*tmp,​p))return 0;
 + tmp=tmp*pri;​
 +
 + return 1;*/
 +}
 +int get_proot(ll p){
 + int g=2;
 + while(!jud_proot(g,​0,​1,​p))g++;​
 + return g;
 +
 +int main(){
 + ll n,p,a;
 + int cas=0,i;
 + sieve();
 + while(~scanf("​%lld%lld%lld",&​n,&​p,&​a)){
 + a%=p;
 + printf("​case%d:​\n",​++cas);​
 + if(!a){
 + if(n==0)printf("​-1\n"​);​
 + else printf("​0\n"​);​
 + continue;​
 +
 + get_pf(p-1);​
 + int g=get_proot(p);​
 + ll y=bsgs(g,​a,​p);​
 + if(y==-1){printf("​-1\n"​);​continue;​} ​
 + // nX+(p-1)Y=y
 + Triple tri=exgcd(n,​p-1);​
 + ll gcd=tri.z;
 + ll X=tri.x;
 + if(y%gcd){printf("​-1\n"​);​continue;​} ​
 + vector<​ll>​ans;​ans.clear();​
 + ll a1=n/​gcd,​a2=(p-1)/​gcd,​a3=y/​gcd;​
 + //​a1*X+a2*Y=a3 (mod a2), (a1,a2)=1
 + ans.pb((mul(X,​a3,​a2)+a2)%a2);​
 + for(i=1;​i<​gcd;​i++)ans.pb(ans[i-1]+a2);​
 + for(i=0;​i<​gcd;​i++)ans[i]=qp(g,​ans[i],​p);​
 + sort(ans.begin(),​ans.end());​
 + for(i=0;​i<​gcd;​i++)printf("​%lld\n",​ans[i]);​
 + }
 + return 0;
 +}
 +</​code>​
 +</​hidden>​
 +
 +\\
 ==== 解法2 ==== ==== 解法2 ====
  
行 71: 行 228:
 现在 $(g^{n})^{y}\equiv a \pmod m$ 式中只有 $y$ 未知。 现在 $(g^{n})^{y}\equiv a \pmod m$ 式中只有 $y$ 未知。
  
-通过 ​$BSGS可以求出所有符合要求的 $y$ ,但这里的 ​$BSGS需要使用 ''​%%map<​int,​vector>​%%''​ ,因为 $g^n$ 不是原根, $a^{t}$ 部分并非一对一关系。过后根据 $y$ 算出 $x$ 即可。+通过 BSGS 可以求出所有符合要求的 $y$ ,但这里的 BSGS 需要使用 ''​%%map<​int,​vector>​%%''​ ,因为 $g^n$ 不是原根, $a^{t}$ 部分并非一对一关系。过后根据 $y$ 算出 $x$ 即可。
  
 这两种解法都通过了原根进行转换,第一种方法由于没有使用较为复杂的 ''​%%map%%''​ ,常数比较小;第二种方法则更加简便、易写。 这两种解法都通过了原根进行转换,第一种方法由于没有使用较为复杂的 ''​%%map%%''​ ,常数比较小;第二种方法则更加简便、易写。
行 77: 行 234:
  
 <hidden 解法2> <hidden 解法2>
-<codedoc ​code:​c++>​+<​code:​c++>​
 #​include<​cstdio>​ #​include<​cstdio>​
 #​include<​algorithm>​ #​include<​algorithm>​
行 214: 行 371:
  return 0;  return 0;
 } }
-</codedoc>+</code>
 </​hidden>​ </​hidden>​
2020-2021/teams/i_dont_know_png/potassium/math_theory_revision_1.1589722112.txt.gz · 最后更改: 2020/05/17 21:28 由 potassium