AOJ 2720 - Identity Function
問題
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; }