给定一个质数 p p p以及一个数列 a i a_i ai,求: ∑ i = 1 n ∑ j = 1 n f ( a i , a j ) f ( a j , a i ) m o d p \sum_{i=1}^n\sum_{j=1}^nf(a_i,a_j)f(a_j,a_i) \mod p ∑i=1n∑j=1nf(ai,aj)f(aj,ai)modp
其中 f ( x , y ) f(x,y) f(x,y)为最小的 i i i满足 ∃ j , x i = y j ( m o d p ) \exist j,x^i=y^j\pmod p ∃j,xi=yj(modp)
n ≤ 1 0 5 n\le 10^5 n≤105
p ≤ 1 0 18 p\le 10^{18} p≤1018
WC的时候见过类似的……然而这次还是没有做出来……其实主要的问题是不会求阶
看到这个东西首先要想到将每个 a i a_i ai的阶求出来。
求之前先将写个Porllad-Rho将 p − 1 p-1 p−1给分解了。想到阶的定义: a a a的阶(记作 o r d ( a ) ord(a) ord(a)为最小的 k k k满足 a k ≡ 1 ( m o d p ) a^k\equiv 1 \pmod p ak≡1(modp),那么把 o r d ( a ) ord(a) ord(a)除以任意一个质因子,这个东西都不成立。
我们考虑分别求 o r d ( a ) ord(a) ord(a)的每个质因子的指数。设现在求质因子 q q q,它在 p − 1 p-1 p−1中的指数为 u i u_i ui,于是我们要求出最小的 t t t满足 a p − 1 q i u q t ≡ 1 ( m o d p ) a^{\frac{p-1}{q^u_i}q^t}\equiv 1 \pmod p aqiup−1qt≡1(modp)。
设 x = a p − 1 q i u x=a^{\frac{p-1}{q^u_i}} x=aqiup−1,暴力枚举 t t t,每次 x → x q x\to x^q x→xq(暴力快速幂),直到它第一次变成 1 1 1为止。
算下时间复杂度: ∣ P ∣ lg p + ∑ u i lg q = ∣ P ∣ lg p + lg p |P|\lg p+\sum u_i\lg q=|P|\lg p + \lg p ∣P∣lgp+∑uilgq=∣P∣lgp+lgp。其中 P P P为 p − 1 p-1 p−1的质因子集合。可以看到后面这一部分根本不是瓶颈。前面算 x x x的那一部分就相对有点慢。(不过前面的那部分可以用个简单的分治来求,但是意义不大,所以就不说了)
接下来就是推式子时间:考虑求 f ( x , y ) f(x,y) f(x,y)的值。设原根为 g g g,则存在 a , b a,b a,b满足 g a ≡ x ( m o d p ) g^a\equiv x \pmod p ga≡x(modp), g b ≡ y ( m o d p ) g^b\equiv y \pmod p gb≡y(modp)。显然 o r d ( x ) = p − 1 gcd ( a , p − 1 ) ord(x)=\frac{p-1}{\gcd(a,p-1)} ord(x)=gcd(a,p−1)p−1。
我们要找到最小的 i i i满足 ∃ j , x i ≡ y j ( m o d p ) \exist j,x^i\equiv y^j \pmod p ∃j,xi≡yj(modp),即 a i ≡ b j ( m o d p − 1 ) ai\equiv bj \pmod {p-1} ai≡bj(modp−1)。
由于 j j j是任取的,用裴蜀定理可以得到 gcd ( b , p − 1 ) ∣ a i \gcd(b,p-1) | ai gcd(b,p−1)∣ai,进而得到 gcd ( b , p − 1 ) gcd ( a , b , p − 1 ) ∣ i \frac{\gcd(b,p-1)}{\gcd(a,b,p-1)}|i gcd(a,b,p−1)gcd(b,p−1)∣i,由于要求最小的 i i i,所以取等号。
f ( x , y ) f ( y , x ) = gcd ( b , p − 1 ) gcd ( a , p − 1 ) g c d ( a , b , p − 1 ) 2 f(x,y)f(y,x)=\frac{\gcd(b,p-1)\gcd(a,p-1)}{gcd(a,b,p-1)^2} f(x,y)f(y,x)=gcd(a,b,p−1)2gcd(b,p−1)gcd(a,p−1)
= p − 1 o r d ( x ) p − 1 o r d ( y ) gcd ( p − 1 o r d ( x ) , p − 1 o r d ( y ) ) 2 =\frac{\frac{p-1}{ord(x)}\frac{p-1}{ord(y)}}{\gcd(\frac{p-1}{ord(x)},\frac{p-1}{ord(y)})^2} =gcd(ord(x)p−1,ord(y)p−1)2ord(x)p−1ord(y)p−1
= l c m ( o r d ( x ) , o r d ( y ) ) 2 o r d ( x ) o r d ( y ) =\frac{lcm(ord(x),ord(y))^2}{ord(x)ord(y)} =ord(x)ord(y)lcm(ord(x),ord(y))2
= o r d ( x ) o r d ( y ) gcd ( o r d ( x ) , o r d ( y ) ) 2 =\frac{ord(x)ord(y)}{\gcd(ord(x),ord(y))^2} =gcd(ord(x),ord(y))2ord(x)ord(y)
也就是求 ∑ d ∑ i ∑ j [ gcd ( a i , a j ) = d ] o r d ( a i ) o r d ( a j ) d 2 \sum_d \sum_i \sum_j [\gcd(a_i,a_j)=d]\frac{ord(a_i)ord(a_j)}{d^2} ∑d∑i∑j[gcd(ai,aj)=d]d2ord(ai)ord(aj)
这个东西可以看成个高维的 m i n min min卷积,于是类似FWT、IFWT做高维前缀和,高维差分即可。
using namespace std;
#include
#include
#include
#include
#include
#include
#include
#include
#define N 100010
#define ll long long
int n;
ll p;
ll a[N],r[N];
ll gcd(ll a,ll b){
while (b){
ll k=a%b;
a=b,b=k;
}
return a;
}
ll multi(ll x,ll y,ll mo){
//x%=mo,y%=mo;
ll z=(long double)x*y/mo;
z=x*y-z*mo;
if (z>=mo) z-=mo;
else if (z<0) z+=mo;
return z;
}
ll qpow(ll x,ll y=p-2,ll mo=p){
x%=mo;
ll r=1;
for (;y;y>>=1,x=multi(x,x,mo))
if (y&1)
r=multi(r,x,mo);
return r;
}
bool mr(ll v){
static int p[10]={2,3,5,7,11,13,17,19,23,29};
for (int i=0;i<10;++i){
if (v==p[i]) return 1;
if (v%p[i]==0) return 0;
}
ll x=v-1,y=0;
for (;!(x&1);x>>=1,++y);
for (int i=0;i<10;++i){
ll t=qpow(p[i],x,v);
if (t==1 || t==v-1) continue;
for (int j=1;j<y;++j){
t=multi(t,t,v);
if (t==v-1) break;
}
if (t!=v-1) return 0;
}
return 1;
}
ll pr(ll n){
while (1){
ll a=rand()%n,b=a,c=rand()%n,s=1;
for (int i=1,k=2;i;++i){
a=(multi(a,a,n)+c)%n;
if (a==b) break;
ll t=multi(s,abs(a-b),n);
if (t==0) return gcd(s,n);
s=t;
if (i==k || !(i&127)){
ll g=gcd(s,n);
if (g!=1) return g;
if (i==k)
k<<=1,b=a;
}
}
}
}
ll q[70],nq,s[70],pro[70];
ll que[70],tail=0;
void divide(ll n){
if (mr(n)){que[++tail]=n;return;}
ll d=pr(n);
divide(d);
divide(n/d);
}
void initp(){
divide(p-1);
sort(que+1,que+tail+1);
for (int i=1;i<=tail;++i){
if (que[i]!=q[nq])
q[++nq]=que[i];
s[nq]++;
}
pro[0]=1;
for (int i=1;i<=nq;++i)
pro[i]=pro[i-1]*(s[i]+1);
}
ll ord[N],id[N];
void calco(ll x,ll &ord,ll &id){
ord=1;
for (int i=1;i<=nq;++i){
ll d=qpow(x,(p-1)/qpow(q[i],s[i]));
if (d==1) continue;
ll qs=d;
for (int t=1;t<=s[i];++t){
qs=qpow(qs,q[i]);
ord*=q[i];
id+=pro[i-1];
if (qs==1) break;
}
}
}
ll g[200000];
void fwt(){
for (int i=1;i<=nq;++i)
for (int j=0;j<pro[nq];j+=pro[i])
for (int t=s[i];t>=1;--t)
for (int k=0;k<pro[i-1];++k)
(g[j+(t-1)*pro[i-1]+k]+=g[j+t*pro[i-1]+k])%=p;
}
void ifwt(){
for (int i=1;i<=nq;++i)
for (int j=0;j<pro[nq];j+=pro[i])
for (int t=1;t<=s[i];++t)
for (int k=0;k<pro[i-1];++k)
(g[j+(t-1)*pro[i-1]+k]+=p-g[j+t*pro[i-1]+k])%=p;
}
ll ans;
void dfs(int x,ll prod,int num){
if (x>nq){
ans+=multi(g[num],qpow(prod,2*(p-2)),p);
return;
}
for (int i=0;i<=s[x];++i,prod*=q[x],num+=pro[x-1])
dfs(x+1,prod,num);
}
int main(){
//freopen("in.txt","r",stdin);
freopen("wlwl.in","r",stdin);
freopen("wlwl.out","w",stdout);
scanf("%d%lld",&n,&p);
for (int i=1;i<=n;++i)
scanf("%lld",&a[i]);
srand(time(0));
initp();
for (int i=1;i<=n;++i){
calco(a[i],ord[i],id[i]);
(g[id[i]]+=ord[i])%=p;
}
fwt();
for (int i=0;i<pro[nq];++i)
g[i]=multi(g[i],g[i],p);
ifwt();
dfs(1,1,0);
ans%=p;
printf("%lld\n",ans);
return 0;
}