こういう問題苦手なんだよね…。
https://yukicoder.me/problems/no/984
問題
素数PとP未満の正整数Nが与えられる。
P-1項からなる数列A[i]=(i+1)*N % Pを考える。
この数列の転倒数を求めよ。
解法
P=2の時は自明。以下、Pが奇数の場合を考える。
A[i]=i+1とすると、B[i]=A[i]*N%Pとなる。
このときAとBの転倒数の偶奇を考える。当然Aの転倒数は0である。
Pの適当な原子根rを取って対数を考えたとき、A'[i]=log(A[i])、B'[i]=log(B[i])とする。
A→A'→B'の転倒数の偶奇の変化とA→B→B'の転倒数の偶奇の変化は等しいはずである。
A→A'とB→B'はいずれもlogを取る作業で同じ置換を取る作業なので、転倒数の偶奇の変化は等しい。
とするとA→BとA'→B'の変化も等しいはずなので、結局A'→B'の転倒数の偶奇の変化が求める会となる。
B'[i]=(A'[i]+log(N))%Pなので、Pが奇数であることを考えるとlog(N)が奇数だと各要素の偶奇が反転し、転倒数の偶奇も反転する。
あとはBaby-Step Giant-Stepでlog(N)を求めればよい。
ll N,P,mo; ll modpow(ll a, ll n = mo-2, ll mo=mo) { ll r=1; while(n) r=r*((n%2)?a:1)%mo,a=a*a%mo,n>>=1; return r; } ll modlog(int g,int a) { // return g^x=a mod a map<int,int> M; ll cur=1; int sq=sqrt(mo); int i; FOR(i,sq) { M[cur]=i; cur=cur*g%mo; } ll step=cur; cur=1; ll num=0; int lp=0; while(1) { ll x=a*modpow(cur)%mo; if(lp++>sq+5) return 1LL<<50; if(M.count(x)) return num+M[x]; cur=cur*step%mo; num+=sq; } } int get_prim(int p) { //primitive element of prime p int q=p-1; vector<int> V; ll x; for(x=2;x*x<=p;x++) if(q%x==0) V.push_back(x),V.push_back(q/x); for(x=2;;x++) { int ng=0; FORR(v,V) if(modpow(x,v,p)==1) { ng=1; break; } if(ng==0) return x; } } void solve() { int i,j,k,l,r,x,y; string s; cin>>P>>N; mo=P; if(N==1 || P==2) return _P("0\n"); x=modlog(get_prim(P),N); cout<<x%2<<endl; }
まとめ
そこでlogを使うとさっと思い浮かばないな…。