G - Discrete Logarithm Problems Editorial by purslane
User EditorialAs you see, the problem is about “Discrete Logarithm”. All the numbers mentioned below are unnegetive intergers.
First of all, define \(\delta_p(x)\) as the minimum positive interger such as \(x^{\delta_p(x)} \equiv 1 \pmod p\),if \(p\) is a prime number. It’s obvious that if \(0 \le i < j < \delta_p(x)\), we can claim that \(x^i \not \equiv x^j \pmod p\). And a famous Femart’s Theorem tells us if \(p \nmid x\), we know \(x^{p-1} \equiv 1 \pmod p\). If \(\delta_p(x) = p-1\), we call \(x\) a Primitive Root of \(p\). There is also a significant property which I won’t prove: if \(x^k \equiv 1 \pmod p\), we know \(\delta_p(x) \mid k\).
If \(t\) is a Primitive Root of \(p\), for all \(1 \le x \le p-1\), there exists a \(k\) such that \(t^k \equiv x \pmod p\), and the \(k\) less than \(p-1\) is unique.
If we know \(t\) and \(x\), to find the \(k\) such that \(t^k = x\), we only need to let \(x = \log_t x\).
In the mod calculations,we can have similar operations. Define \(\text{ind}_t x\) as the minumum positive value such as \(t^{\text{ind}_t x} \equiv x \pmod p\), if \(t\) is a Primitive Root of \(p\).
Note that in most places, \(\text{ind}_t 1 =0\). But here I make it be \(p-1\) to make the problem easy to solve.
So the problem is equal to find pairs \((i,j)\) such as:
- \(1 \le i , j \le n\).
- There is a positive \(k\) such that \(k \times \text{ind}_t a_i \equiv \text{ind}_t a_j \pmod {p-1}\).
The proof is for exercise.
Let \(b_i\) be \(\gcd(\text{ind}_t a_i,p-1)\), the problem is also equal to:
- \(1 \le i , j \le n\).
- \(b_i | b_j\).
And most importantly, \(\dfrac{p-1}{b_i} = \delta_p (a_i)\).
Proof is for exercise.
So we can calculate \(\delta_p(a_i)\) ( I won’t explain. You can search the internet ), then we know \(b_i\).
Notice that \(b_i | p-1\), so there are at most \(d(p-1) \le 1.1 \times 10^4\) different \(b\).
We can solve that in a time complexity of \(O(\sqrt p + n \log^2 p + d^2(p-1))\).
Code :
#include<bits/stdc++.h>
#define int long long
#define ffor(i,a,b) for(int i=(a);i<=(b);i++)
#define roff(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
const int MAXN=2e5+10;
int n,p,a[MAXN];
int qpow(__int128 base,int p,int mod) {
__int128 ans=1;
while(p) {
if(p&1) ans=ans*base%mod;
base=base*base%mod,p>>=1;
}
return ans;
}
vector<int> frac;
map<int,int> mp;
int calc_b(int v) {
int ans=p-1;
for(auto id:frac) if(qpow(v,ans/id,p)==1) ans/=id;
return (p-1)/ans;
}
signed main() {
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>p; ffor(i,1,n) cin>>a[i];
int v=p-1;
ffor(i,2,v/i) while(v%i==0) frac.push_back(i),v/=i;
if(v) frac.push_back(v);
sort(frac.begin(),frac.end());
ffor(i,1,n) mp[calc_b(a[i])]++;
int ans=0;
for(auto pr1:mp) for(auto pr2:mp) if(pr1.first%pr2.first==0) ans+=pr1.second*pr2.second;
cout<<ans;
return 0;
}
Refference : OI-Wiki
Chinese version:Link
posted:
last update: