問題
Identity Function | Aizu Online Judge
問題概要
1より大きい整数が与えられる。次のような関数を考える:
この時、の
について
が成り立つような最小の
を求めよ。このような
が存在しない時は、
-1を出力せよ。
アイデア
まず、考察したこととしては、が素数の時は、フェルマーの小定理から
であることがわかるので素数の時は1が答えになると思った。ただ、素数で無いときにも答えが1になることはあるようでこのような条件を満たす数にはカーマイケル数という名前がついているらしい。
は漸化式の形になっているが、これは帰納的に
であることが示せる。
を素数として、
のとき、
となることはない。こうなるということは、
と書けるということだが、
とくくると、この数は
しか素因数を持たないはずなのに、括弧内の数はどのように
を選んだとしても
の倍数になることはないからである。
つまり、これ以降で考えるべきとしては
のように、各素因数が1つのみである素数の積の形をしている。
さて、が成り立つためには、各素数
でも
が成り立っているはずである。さらに、これは
の
について成り立っているので(
と
が互いに素なときでも成立しなければならない)、指数の肩に注目すると
と言い換える事ができる。
これがの
について成り立っていて欲しい、ということはLCMを考えて
で割り切れて欲しいということになる。よって、この問題は
を満たす最小の
を見つける、と言う問題になった。
まず、と
が互いに素でないならこのような
は存在しない。そして互いに素である場合には、この
はオイラー関数
の約数である(!?)ので、約数を列挙して順番に試せば良い。
とあるが、なんで約数しかみなくていいんだ...ってなったが、もう一度求めたいものをみてみると、この求めたいというのはまさにカーマイケルのλ関数によって与えられるものであり、それがオイラー関数の約数であることは定義より明らかである(オイラー関数は単純に掛け算しているところで、カーマイケルのλ関数はLCMを取っているので)(参考)。
ただ、今回はNについてのみ成り立ってくれればいいので、やはり約数を小さい方から順にチェックする必要がある。
実装(C++)
#include <bits/stdc++.h> using namespace std; using ll = long long; #define rep(i,n) for(int (i)=0;(i)<(int)(n);++(i)) #define all(x) (x).begin(),(x).end() #define pb push_back #define fi first #define se second #define dbg(x) cout<<#x" = "<<((x))<<endl template<class T,class U> ostream& operator<<(ostream& o, const pair<T,U> &p){o<<"("<<p.fi<<","<<p.se<<")";return o;} template<class T> ostream& operator<<(ostream& o, const vector<T> &v){o<<"[";for(T t:v){o<<t<<",";}o<<"]";return o;} int lcm(int x, int y){ return x/__gcd(x,y)*y; } int euler_phi(int n){ if(n==0) return 0; int ret = n; for(int i=2; i*i<=n; ++i){ if(n%i==0){ ret -= ret/i; while(n%i==0) n/=i; } } if(n!=1) ret -= ret/n; return ret; } map<int,int> factorize(int n){ map<int,int> ret; for(int i=2; i*i<=n; ++i){ while(n%i==0){ n/=i; ++ret[i]; } } if(n>1) ++ret[n]; return ret; } ll mod_pow(ll x, ll n, ll mod){ ll ret = 1; while(n){ if(n&1) (ret*=x)%=mod; (x*=x)%=mod; n>>=1; } return ret; } int main(){ int n; cin >>n; map<int,int> f = factorize(n); int L = 1; for(const auto &p:f){ if(p.se>1){ cout << -1 << endl; return 0; } L = lcm(L, p.fi-1); } if(__gcd(n,L)!=1){ cout << -1 << endl; return 0; } int ans = L; int P = euler_phi(L); for(int i=1; i*i<=P; ++i){ if(P%i==0){ for(int d:{i,P/i}){ if(mod_pow(n,d,L) == 1) ans = min(ans,d); } } } cout << ans << endl; return 0; }