题意:
你有一个随机数生成器,最初给定一个 \(0\leq
x\leq n-1\) 的整数作为随机种子.
这个随机数生成器会每次输出 \(x\)
并将 \(x^k  \bmod n\) 作为新的 \(x\)。
你很快发现这个随机数生成器很渣。为了证明它很渣,你想要求出有多少个随机种子,使得这个随机数生成器会输出初始种子无穷多次。
数据范围:\(n,k\leq 10^{18}\)。
1 2 3 4 5 6 7 8 9 10 11
   | Sample.in n,k Sample.out ans
  ------
  Sample.in 10 2 Sample.out 4
 
  | 
 
首先,我们注意这样一个事。
当 \(n\in \text{prime}\)
怎么做?
方程怎么写?
\[
x^{k^t}\equiv x\pmod{n}
\]
那么由于无论何时 \((x,n)=1\),阶始终存在,所以有:
\[
k^t\equiv 1\pmod{ord_n(x)}
\]
也就是说:
\[
\gcd(k,ord_n(x))=1
\]
当我们知道阶为多少时,我们显然可以求出这个集合的大小。
我们现在枚举阶:
\[
\begin{aligned}
\sum_{d\mid\phi(n)} \phi(d)[\gcd(d,k)=1]\\
\sum_{d\mid \phi(n),\gcd(d,k)=1}\phi(d)
\end{aligned}
\]
显然我们令 \(T\) 为 \(\phi(n)\) 剔除所有 \(k\) 因子的结果。
\(T\) 的求法可以用以下代码体现:
1 2 3
   | while(gcd(phi,k)!=1){     phi/=gcd(phi,k); }
 
  | 
那么此时狮子可化简为:
\[
\begin{aligned}
\sum_{d\mid T} \phi(d)=T
\end{aligned}
\]
这是好的,那么我们有没有办法使得 \(n\not
\in \text{prime}\) 使用类似方法做呢?
考虑如果对于 \(n\not \in
\text{prime}\) 如果我们钦定 \(\gcd(x,n)=1\),上述做法依然成立。
现在问题被这个 \(\gcd\)
卡住了,那就考虑 \(\gcd\)
会产生什么厉害东西。
\(g=\gcd(x,n)\),我们考虑这样一个事,如果存在
\(p\),使得 \(p^a\mid\mid x,p^b\mid\mid n,a<b\)。
那么这样 \(x^d\),\(d\) 足够大时,\(p^b\mid x^d\),这样 \(x^d\) 不可能和 \(x\) 同余。
所以 \(g,x,n\) 存在关系。
\(\gcd(g,\frac{n}{g})=1,\gcd(g,\frac{x}{g})=1\)。
那么设 \(x=g\cdot X,n=g\cdot N\)
\[
\begin{aligned}
x^{k^t}\equiv x\pmod{n}\\
(gX)^{k^t}\equiv gX\pmod{gN}\\
g^{k^t}X^{k^t}\equiv gX\pmod{gN}\\
\end{aligned}
\]
等式两边同除 \(g\)。
$$
\[\begin{aligned}
g^{k^t-1}X^{k^t}\equiv X\pmod{N}\\
g^{k^t-1}X^{k^t-1}\equiv 1\pmod{N}\\
k^t-1\equiv 0\pmod{ord_{N}(gX)}\\
k^t\equiv 1\pmod{ord_{N}(gX)}
\end{aligned}\]
$$
很眼熟是吧。
由于这个 \(gX\)
似乎并不那么平凡,我们只能先猜测这个的答案就是上面的答案。
也就是猜测 \(gX\) 与满足 \(k^t\equiv1\pmod{ord_{N}(x)}\) 的 \(x\in[1,N)\) 一一对应。
显然 \(ord_{N}(gX)=ord_N(gX\bmod
N),\gcd(gX,N)=1\),所以每一个 \(gX\) 能仅对应一个满足的 \(x\)。
接下来考虑 \(gX\) 在 \(\bmod N\) 意义下互不相等。
反证法:如果 \(gA\equiv gB\pmod{N},gA\not
\equiv gB\pmod{n}\),直接产生矛盾。(有关 \(A\equiv B\pmod{N}\) 的矛盾)
接下来考虑是否每一个满足的 \(x\)
都有一个 \(gX\) 与之对应。
因为 \(\gcd(g,N)=1\)
显然存在逆元。
\(gX\equiv
x\pmod{N},X=g^{-1}x\pmod{N}\)
显然能够对应。
所以当 \(g\) 确定时,\(k^t\equiv 1\pmod{ord_{N}(gX)}\) 满足的
\(gX\) 个数就是上面所说的 \(x\) 的个数。
所以我们可以枚举 \(g\),从而算出结果。
复杂度 \(\mathcal{O(pollard-rho(n)+2^{\omega(n)}\log^2(n))}\)
后面可能是单 \(\log\)
但问题不大,复杂度瓶颈在于 \(pollard-rho(n)\),即分解质因数复杂度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
   | 
  #include<bits/stdc++.h> using namespace std; typedef long long ll; inline ll mul(ll a,ll b,ll mod){     if(a>=mod) a%=mod;     if(b>=mod) b%=mod;     ll tmp=a*b-ll((long double)a/mod*b+0.5)*mod;     return tmp<0?tmp+mod:tmp; } ll p[]={3,5,7,11,13,17,19,23,29,31}; inline ll qpow(ll a,ll b,ll mod){     ll k=1;     while(b){         if(b&1) k=mul(k,a,mod);         a=mul(a,a,mod);b>>=1;     }return k; } inline bool chk(ll mod,ll p){     ll res=qpow(p,mod-1,mod);     ll b=mod-1;     if(res!=1) return 0;          while(b%2==0){         b/=2;         ll r=qpow(p,b,mod);         if(r==mod-1) return 1;         if(r!=1) return 0;     }     return 1; } inline bool prime(ll x){     if(x==2) return 1;     for(int i=0;i<9;i++){                  if(x==p[i]) return 1;         if(x%p[i]==0) return 0;         if(chk(x,p[i])==0) return 0;     }return 1; }
  inline ll nxt(ll a,ll c,ll mod){     ll tmp=mul(a,a,mod)+c;     return tmp>=mod?tmp-mod:tmp; } inline ll gcd(ll a,ll b){     if(!b) return a;     return gcd(b,a%b); } inline ll pollard(ll n){     if(n==4) return 2;     ll a1=1ll*rand()*rand()%n,c=1ll*rand()*rand()%n;     ll a2=a1;     a1=nxt(a1,c,n),a2=nxt(nxt(a2,c,n),c,n);     int lim=1;     while(a1!=a2){         ll v=1;         for(int i=1;i<=lim&&a1!=a2;i++,a1=nxt(a1,c,n),a2=nxt(nxt(a2,c,n),c,n)){             ll backv=v;             v=mul(v,abs(a1-a2),n);             if(!v) return gcd(abs(a1-a2),n);         }         ll g=gcd(v,n);         if(g>1) return g;         if(lim<128) lim<<=1;     }     return pollard(n); } vector<ll> f; inline void factor(ll n){     if(prime(n)){         f.push_back(n);         return ;     }     ll fac=pollard(n);     if(prime(fac)){         f.push_back(fac);         return factor(n/fac);     }     factor(fac);     factor(n/fac); } ll n,k; inline ll getv(ll v,ll ph){     while(gcd(ph,k)!=1){         ph/=gcd(ph,k);     }     return ph; } vector<ll> a,b; int lim; ll ans; inline void dfs(int n,ll res,ll ph){     if(n==a.size()){         if(res>1) ans+=getv(res,ph);         return ;     }     dfs(n+1,res,ph);     dfs(n+1,res*a[n],ph*b[n]); } signed main(){     cin>>n>>k;     f.clear();     factor(n);     ll N=n;     for(int i=1;i<f.size();i++){         if(f[i]!=f[i-1]){             ll res=1;             while(N%f[i-1]==0) N/=f[i-1],res=res*f[i-1];             a.push_back(res);             b.push_back(res/f[i-1]*(f[i-1]-1));         }     }     a.push_back(N);     b.push_back(N/(f.back())*(f.back()-1));     lim=a.size();     dfs(0,1ll,1ll);     cout<<ans+1; }
 
 
  |