类欧几里得算法
扩展欧拉定理(对于a,p不互质的情况计算a%p):https://blog.csdn.net/hzj1054689699/article/details/80693756
二次剩余
https://blog.csdn.net/kele52he/article/details/78897187
对于n整数a若存在整数x 满足 x^2%n=a那么称a是mod n 的二次剩余
判定:当且仅当 a^(p-1)/2=1
引理:对于奇素数p下的二次剩余n,有两个不同的x满足x^2%p=n
一个奇素数的完全剩余系下有(p-1)/2 个二次剩余。
Cipolla 算法
求解 x^2=n mod p, O ( n log n ) O(n\log n) O(nlogn)。
步骤:
1.随机一个 a,使得
(
a
2
−
n
)
(
p
−
1
)
/
2
=
−
1
m
o
d
p
(a^2-n)^{(p-1)/2}=-1\mod p
(a2−n)(p−1)/2=−1modp,期望次数 2 次。
2.令
ω
=
a
2
−
n
\omega=\sqrt{a^2-n}
ω=a2−n,扩域为虚根。
3.
(
a
+
ω
)
(
p
+
1
)
/
2
(a+\omega)^{(p+1)/2}
(a+ω)(p+1)/2 即为所求。
//xy%p=xy-(xy)/p*p
inline ll qmul(ll a,ll b,ll mod)
{
a%=mod,b%=mod;
return (((a*b)-(ll)((ll)((long double)a/mod*b+1e-3)*mod))%mod+mod)%mod;
}
扩展BSGS
狄利克雷卷积的逆
f
∗
f
−
1
=
e
f*f^{-1}=e
f∗f−1=e。通过构造,可以
O
(
n
log
n
)
O(n\log n)
O(nlogn) 构造出函数的逆。
如果积性函数
f
f
f 和
g
g
g 对于任意质数
p
p
p 满足
f
(
p
)
=
g
(
p
)
=
1
f(p)=g(p)=1
f(p)=g(p)=1,我们就可以快速求出
f
f
f 的前缀和。
设
h
=
f
∗
g
−
1
h=f*g^{-1}
h=f∗g−1,那么就有
f
(
p
)
=
g
(
1
)
h
(
p
)
+
g
(
p
)
h
(
1
)
=
h
(
p
)
+
1
=
1
f(p)=g(1)h(p)+g(p)h(1)=h(p)+1=1
f(p)=g(1)h(p)+g(p)h(1)=h(p)+1=1,推出
h
(
p
)
=
0
h(p)=0
h(p)=0。
我们要求的是:
∑
i
=
1
n
f
(
i
)
=
∑
i
=
1
n
∑
d
∣
i
g
(
d
)
h
(
i
d
)
=
∑
d
=
1
n
h
(
d
)
∑
i
=
1
n
/
d
g
(
i
)
\sum_{i=1}^n f(i) = \sum_{i=1}^n\sum_{d|i}g(d)h(\frac i d) = \sum_{d=1}^nh(d)\sum_{i=1}^{n/d} g(i)
i=1∑nf(i)=i=1∑nd∣i∑g(d)h(di)=d=1∑nh(d)i=1∑n/dg(i)
由于
h
h
h 只在所有质因子次数都大于 1 的情况下才不为零,这样的数可以写成
a
2
b
3
a^2b^3
a2b3 的形式,不超过
O
(
n
)
O(\sqrt n)
O(n) 个。枚举
a
a
a,枚举
b
b
b,就得到了
a
2
b
3
a^2b^3
a2b3 的质因数分解,即可快速计算。
狄利克雷生成函数
F
(
s
)
=
∑
i
=
0
∞
f
(
i
)
/
i
s
F(s)=\sum_{i=0}^{\infin}f(i)/i^s
F(s)=i=0∑∞f(i)/is
多项式卷积即为狄利克雷卷积。
定义求导算子 ′ ' ′:当 p p p 为质数时, p ′ = 1 p'=1 p′=1,对于整数 a a a, b b b,有 ( a b ) ′ = a ′ b + a b ′ (ab)'=a'b+ab' (ab)′=a′b+ab′。
1
求
∑
i
=
1
n
gcd
(
i
,
i
′
)
\sum_{i=1}^n \gcd(i,i')
∑i=1ngcd(i,i′)。
??? ??? ???
min_25 筛
作用:计算积性函数前缀和。复杂度
O
(
n
3
/
4
log
n
)
O(\frac{n^{3/4}}{\log n})
O(lognn3/4)。
要求:
f
(
p
)
f(p)
f(p) 为关于
p
p
p 的低次多项式,
f
(
p
c
)
f(p^c)
f(pc) 能很快计算。
第一步
首先我们想要求所有质数的函数值。构造完全积性函数 F ( n ) F(n) F(n),满足 F ( p ) = f ( p ) F(p) = f(p) F(p)=f(p)。记 g ( n , j ) = ∑ i = 1 n [ m i n d ( i ) > p j ] F ( i ) g(n,j)=\sum_{i=1}^n [mind(i)>p_j]~F(i) g(n,j)=∑i=1n[mind(i)>pj] F(i) , m i n d ( i ) mind(i) mind(i) 表示 i i i 的最小质因子。换句话说统计的是质数和最小值因子大于 p j p_j pj 的数的贡献。我们的目的便是求 g ( n , ∣ P ∣ ) g(n, |P|) g(n,∣P∣)。
构造完全积性函数的目的一是使 g ( n , 0 ) g(n, 0) g(n,0) 好算。比如如果 f ( p ) f(p) f(p) 是低次单项式,那么 g ( n , 0 ) g(n, 0) g(n,0) 就是自然数幂和;如果 f ( p ) f(p) f(p) 是低次多项式,因为我们是求和,因此可以拆成若干个单项式再相加,转化为上一种情况。
初始,我们知道 g ( n , 0 ) g(n,0) g(n,0) 的值,考虑用质数把合数的贡献筛掉。构造完全积性函数的目的之二就是方便我们能够筛掉合数的贡献。我们按照 j j j 从小到大的顺序转移 g g g。考虑 g ( n , j − 1 ) g(n,j-1) g(n,j−1) 转移到 g ( n , j ) g(n,j) g(n,j),有哪些数变得不合法了。就是那些最小质因子刚好为 p j p_j pj 的那些数。
注意到,如果
p
j
2
p_j^2
pj2 如果大于
x
x
x,由于没有小于
x
x
x 的合数含有
p
j
p_j
pj,因此直接转移过来。
g
(
n
,
j
)
=
{
g
(
n
,
j
−
1
)
−
p
j
k
(
g
(
n
/
p
j
,
j
−
1
)
−
g
(
p
j
−
1
,
j
−
1
)
)
,
p
j
2
≤
n
g
(
n
,
j
−
1
)
,
p
j
2
>
n
g(n,j)= \begin{cases} g(n,j-1)-p_j^k\big(g(n/p_j,j-1)-g(p_j-1,j-1)\big)&,p_j^2\leq n\\ g(n,j-1)&,p_j^2>n \end{cases}
g(n,j)={g(n,j−1)−pjk(g(n/pj,j−1)−g(pj−1,j−1))g(n,j−1),pj2≤n,pj2>n
第一维只有根号
n
n
n 大小。发现求
g
g
g 的过程与原函数是不是积性函数没关系(
F
(
x
)
F(x)
F(x) 是我们构造的),因此可以求
1
1
1 到
n
n
n 质数个数。
第二步
考虑用这个东西求函数的前缀和。定义 s ( n , j ) = ∑ i = 1 n [ m i n d ( i ) ≥ p j ] f ( i ) s(n,j)=\sum_{i=1}^n[mind(i)\geq p_j]~f(i) s(n,j)=∑i=1n[mind(i)≥pj] f(i)。目标是求 s ( n , 1 ) s(n,1) s(n,1),边界条件是 s ( n , ∣ p ∣ ) s(n,|p|) s(n,∣p∣) 为所有质数的答案。因此我们递归计算 s s s。转移的时候枚举最小质因子,枚举最小质因子 p k p_k pk 的次数 e e e, s [ n / p k e ] [ k + 1 ] s[n/p_k^e][k+1] s[n/pke][k+1]。注意单独考虑 f ( p k e ) f(p_k^e) f(pke)。
s ( n , j ) = g ( n , ∣ p ∣ ) − ∑ i = 1 j − 1 f ( p i ) + ∑ k ≥ j ∑ e ( f ( p k e ) s ( n / p k e , k + 1 ) + f ( p k e + 1 ) ) s(n,j)=g(n,|p|)-\sum_{i=1}^{j-1}f(p_i)+\sum_{k\geq j}\sum_e\big(f(p_k^e)s(n/p_k^e,k+1)+f(p_k^{e+1}) \big) s(n,j)=g(n,∣p∣)−i=1∑j−1f(pi)+k≥j∑e∑(f(pke)s(n/pke,k+1)+f(pke+1))
Loj6053 简单的函数
Notice: 1 拿出来最后单独统计,val 数组里的值从大到小,数组开到两倍根号。
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define ld long double
using namespace std;
const int mod=1e9+7,M=200010,inv2=(mod+1)/2;
int ind1[M],ind2[M];
bool np[M];
ll sum[M],p[M],cnt,tot,h[M],g[M],val[M],n,B;
int read()
{
int x=0;char c=getchar(),flag='+';
while(!isdigit(c)) flag=c,c=getchar();
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return flag=='-'?-x:x;
}
void init(int n)
{
np[1]=1;
for(int i=2;i<=n;i++)
{
if(!np[i]) p[++cnt]=i;
for(int j=1;j<=cnt&&i*p[j]<=n;j++)
{
np[i*p[j]]=1;
if(i%p[j]==0) break;
}
}
for(int i=1;i<=cnt;i++) sum[i]=(sum[i-1]+p[i])%mod;
}
ll S(ll x,int j)
{
if(x<=1||x<p[j]) return 0;
int t=(x<=B)?ind1[x]:ind2[n/x];
ll ans=g[t]-sum[j-1]-h[t]+(j-1);
if(j==1) ans+=2;
for(int k=j;k<=cnt&&p[k]*p[k]<=x;k++)
{
ll t1=p[k],t2=p[k]*p[k];
for(int e=1;t2<=x;e++,t1=t2,t2*=p[k])
ans=(ans+(p[k]^e)*S(x/t1,k+1)+(p[k]^(e+1)))%mod;
}
return ans;
}
int main()
{
cin>>n;
init(B=sqrt(n)+1);
for(ll l=1,r;l<=n;l=r+1)
{
r=n/(n/l);
val[++tot]=n/l;
if(n/l<=B) ind1[n/l]=tot;
else ind2[r]=tot;
ll t=(n/l)%mod;
h[tot]=(t-1)%mod;
g[tot]=(2+t)*(t-1)%mod*inv2%mod;
}
for(int j=1;j<=cnt;j++)
{
for(int i=1;i<=tot&&p[j]*p[j]<=val[i];i++)
{
int t=(val[i]/p[j]<=B)?ind1[val[i]/p[j]]:ind2[n/(val[i]/p[j])];
g[i]=(g[i]-p[j]*(g[t]-sum[j-1]))%mod;
h[i]=(h[i]-h[t]+j-1)%mod;
}
}
cout<<(S(n,1)%mod+1+mod)%mod;
return 0;
}
/*by DT_Kang*/