6782. 2020.08.06【NOI2020】模拟T3 乌拉乌拉

题目

给定一个质数 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=1nj=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 n105

p ≤ 1 0 18 p\le 10^{18} p1018


正解

WC的时候见过类似的……然而这次还是没有做出来……其实主要的问题是不会求阶

看到这个东西首先要想到将每个 a i a_i ai的阶求出来。

求之前先将写个Porllad-Rho将 p − 1 p-1 p1给分解了。想到阶的定义: 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 ak1(modp),那么把 o r d ( a ) ord(a) ord(a)除以任意一个质因子,这个东西都不成立。

我们考虑分别求 o r d ( a ) ord(a) ord(a)的每个质因子的指数。设现在求质因子 q q q,它在 p − 1 p-1 p1中的指数为 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 aqiup1qt1(modp)

x = a p − 1 q i u x=a^{\frac{p-1}{q^u_i}} x=aqiup1,暴力枚举 t t t,每次 x → x q x\to x^q xxq(暴力快速幂),直到它第一次变成 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 Plgp+uilgq=Plgp+lgp。其中 P P P p − 1 p-1 p1的质因子集合。可以看到后面这一部分根本不是瓶颈。前面算 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 gax(modp) g b ≡ y ( m o d p ) g^b\equiv y \pmod p gby(modp)。显然 o r d ( x ) = p − 1 gcd ⁡ ( a , p − 1 ) ord(x)=\frac{p-1}{\gcd(a,p-1)} ord(x)=gcd(a,p1)p1

我们要找到最小的 i i i满足 ∃ j , x i ≡ y j ( m o d p ) \exist j,x^i\equiv y^j \pmod p j,xiyj(modp),即 a i ≡ b j ( m o d p − 1 ) ai\equiv bj \pmod {p-1} aibj(modp1)

由于 j j j是任取的,用裴蜀定理可以得到 gcd ⁡ ( b , p − 1 ) ∣ a i \gcd(b,p-1) | ai gcd(b,p1)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,p1)gcd(b,p1)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,p1)2gcd(b,p1)gcd(a,p1)

= 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)p1,ord(y)p1)2ord(x)p1ord(y)p1

= 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} dij[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;
}

你可能感兴趣的:(数学)