简单的数论函数及其求和方法

数论函数#

记号与约定#

称函数 f 为数论函数,当且仅当其定义域为 Z,陪域为 C

称数论函数 f 为积性函数当且仅当对于任意 a,bN+,gcd(a,b)=1,有 f(ab)=f(a)f(b)

称数论函数 f 为完全积性函数当且仅当对于任意 a,bN+,有 f(ab)=f(a)f(b)

按照如下记号记录部分数论函数:

  • I(n)=1
  • ϵ(n)=[n=1]
  • id(n)=n
  • idk(n)=nk
  • d(n)=dn1
  • σ(n)=dnd
  • μ,φ 定义照常,不再赘述。

其中 I,ϵ,id,idk 完全积性

另外,文中部分 a/b,ab 型分数根据上下文可能有向下取整(ab)的含义,请注意甄别。

线性筛#

对于任意积性函数,我们可以线性筛求出。

我们只需要关心积性函数在素数幂 pk 处的取值。为了检验一个数 i 是否是 pk 型的,我们可以维护 low(i) 表示 i 中最小质因子的部分。例如 low(6)=2,low(49)=49,low(12)=4。当且仅当 i=low(i)ipk 型的。

具体的 kp 也是不难维护的。

下面的模板来自 Alex-Wei

for(int i = 2; i < N; i++) {
  if(!vis[i]) pr[++pcnt] = i, f[i] = ..., low[i] = i; // 单独算 f(p)
  for(int j = 1; j <= pcnt && i * pr[j] < N; j++) {
    vis[i * pr[j]] = 1;
    if(i % pr[j] == 0) { // i 与 p 不互质
      low[i * pr[j]] = low[i] * pr[j];
      if(i == low[i]) f[i * pr[j]] = ...; // i = p ^ k,单独算 f(p ^ {k + 1})
      else f[i * pr[j]] = f[i / low[i]] * f[low[i * pr[j]]];
      break;
    }
   	low[i * pr[j]] = pr[j];
    f[i * pr[j]] = f[i] * f[pr[j]]; // i 与 p 互质,f(ip) = f(i)f(p) 
  }
}

两例变量分离#

  • d(ij)

    d(ij)=xiyj[gcd(x,y)=1]

    构造双射容易证明。

  • φ(ij)

    φ(ij)=φ(i)φ(j)gcd(i,j)φ(gcd(i,j))

    证明:

    φ(i)φ(j)gcd(i,j)=gcd(i,j)(ipi,pPp1p)(jqj,qPq1q)=ijgcd(i,j)(pij,pPp1p)(qgcd(i,j),qPq1q)=φ(ij)φ(gcd(i,j))

关于 μ2(i)#

有性质:

μ2(i)=d2iμ(d)

μ(i)0 时,必然不存在平方因子,因此恰有 d=1 造成 1 的贡献。

μ(i)=0 时,设 p1,p2,,pk 这些质因子在 i 中的次数至少为 2d 要在右侧造成贡献,只能是这些质因子构成的集合的某个子集的乘积,根据二项式定理,贡献为 0

狄利克雷卷积#

狄利克雷卷积#

狄利克雷卷积:是一个算子。对于数论函数 f,g,我们有

(fg)(n)=dnf(n)g(nd)

狄利克雷逆:若 fg=ϵ 则称 f,g 互为狄利克雷逆。

对于 f(1)0ff 的狄利克雷逆一定存在。显然这条件已经必要。如下推导得到 g

ϵ(n)=d|nf(d)g(nd)f(1)g(n)=ϵ(n)dn,d>1f(d)g(nd)g(n)=1f(1)(ϵ(n)dn,d>1f(d)g(nd))

基本性质#

  • 交换律

    fg=gf

  • 结合律

    (fg)h=f(gh)

  • 分配律

    (f±g)h=fh±gh

  • 消去律

    fh=ghf=g  (h(1)0)

  • 两个积性函数的狄利克雷卷积是积性函数

  • 积性函数的狄利克雷逆是积性函数

几个简单的狄利克雷卷积#

  • μI=ϵ

    这也是为什么我们可以把 [gcd(x,y)=1] 化开变成 dgcd(x,y)μ(d)

  • φI=id

    这也是为什么我们可以把 gcd(x,y) 化开变成 dgcd(x,y)φ(d)

  • II=d

    根据 d 的定义。

  • idI=σ

    根据 σ 的定义。

狄利克雷卷积的求法 (特别鸣谢: pp_orange)#

f,g 已知,每一项可以 O(1) 求出。设我们要计算 h=fg 的前 n 项。

f,g 是普通函数时#

采用定义式 O(nlogn) 计算。

Code:[V 我 50 我帮你写]。

f 为积性函数时#

考察 f=I 这一特例。此时等价于狄利克雷前缀和,复杂度为 O(nloglogn)

f 为任意积性函数时,我们可以执行类似的操作。考虑枚举质数 p。初始我们的卷积中只有 h(n)=f(1)g(n) 这一项。每枚举一个质数 p,我们就枚举不含因子 p 的正整数 n,把 f(p)h(n),f(p2)h(n),f(p3)h(n), (pn) 这样的项贡献到对应的新的 h(np),h(np2),h(np3), 上。这样我们就相当于在 p 这个维度做了一遍前缀和,现在 h(n) 里面包含了所有 f(x)g(n/x) 的项,其中 x 整除 n,且 x 的质因子集合中只有我们枚举过的质数。

因为高维前缀和是对的,那么这个操作也应当是对的。此时复杂度为 1pkn,k1,pPnpk=O(nloglogn)。这是因为对于每个 p,后面的部分都可以看作余项(等比数列求和不超过常数倍的第一项),因此其复杂度和狄利克雷前缀和 / 埃氏筛法相同。

Code:[V 我 50 我帮你写]。

f,g 为积性函数时#

时间复杂度为 O(n)

我们只需要求出 h 在素数幂处的取值。对于每个素数直接暴力,小于等于 n 的素数复杂度可以看成 O(nlogn×log2n)=O(nlogn),而对于大于 n 的素数只会有一项。因此,计算素数幂处取值的复杂度可以看作低阶项,不影响用 O() 计算的复杂度。

Code:[V 我 50 我帮你写]。

杜教筛#

前置:i=1nik  (k4) 公式#

  • k=1n(n+1)/2
  • k=2n(n+1)(2n+1)/6
  • k=3n2(n+1)2/4
  • k=4n(n+1)(2n+1)(3n2+3n1)/30。已经很少用到了。
  • 对于 k>4,使用连续点值拉格朗日插值可以 O(k) 计算一次。

流程#

考虑求某数论函数 f 的前缀和 S(n)=i=1nf(i)。杜教筛的想法是通过一个比较好的数论函数 g 和比较好的 fgf 的前缀和化简。这和 f,g,fg 是否是积性函数无关。

我们知道

i=1n(fg)(i)=i=1ng(i)S(ni)g(1)S(n)=i=1n(fg)(i)i=2ng(i)S(ni)

如果 fgg 的前缀和比较简单,我们就可以快速求出 f 的前缀和了。

考虑如下伪代码:

inline int SumF(int n){ // 注意:这里计算的是 g(1)S(n). 但大部分时候 g 是积性函数或其点乘,因此 g(1)=1.
	int ans=SumFG(n);
	for(int l=2,r;l<=n;l=r+1){
		r=n/(n/l);
		ans-=(SumG(r)-SumG(L-1))*SumF(n/l);
	}
	return ans;
}

当使用记忆化的时候,根据整除分块经典结论,我们只会用到不超过 2n 个值。

那么 T(n)=O(n)+i=1n(O(i)+O(ni))。求和改积分容易证得 T(n)=O(n3/4)

否则时间复杂度不详(OI-Wiki 对不加以记忆化的时间复杂度证明作出了证伪:不能直接将第二层之后的 T(nij) 看作余项)。

如果我们预处理部分 S(i)(i=1,2,,m)  (mn),设预处理复杂度为 T0(m),则总复杂度为

T(n)=T0(m)+k{n/x},k>mT(k)T0(m)+k=1n/mO(nk)=O(T0(m)+nm)

那么一般 T0(m)=O(m),此时 m=Θ(n2/3) 最优。

详见 OI-Wiki。

两例简单的杜教筛#

  • f=μ

    根据 μI=ϵ,取 f=μ,g=I,fg=ϵ

  • f=φ

    根据 φI=id,取 f=φ,g=I,fg=id

点乘#

定义 f,g 点乘的结果 fg 是一个函数,满足 (fg)(n)=f(n)g(n)

对于数论函数 A,B,C,当 C完全积性函数的时候,我们有

(AC)(BC)=(AB)C

那么当我们要求前缀和的函数 f 为两个简单函数点乘的时候,我们可以运用这个等式,从而寻求形式较好的 gfg,然后杜教筛解决。

下面是两例比较基本的点乘处理方法。

  • f=μidk:取 g=idk

    h=(μidk)idk=(μidk)(Iidk)=(μI)idk=ϵ

  • f=φidk:取 g=idk

    h=(φidk)idk=(φidk)(Iidk)=(φI)idk=idk+1

下面是抄的一例点乘:

  • f=μ2(μid):取 g=id

    h=μ2(μid)id=μ2((μI)id)=μ2ϵ=μ2

    μ2 的前缀和可以通过公式 μ2(i)=d2iμ(d) 计算:简单地交换求和号,前缀和变为 d=1nμ(d)nd2。直接计算即可做到 O(n),求出 μ(i) 的前缀和可以进一步做到 O(n1/3)。但是在低于 O(n) 时间复杂度内求出 μ(i) 的前缀和需要再写筛法,其实就不如直接 O(n) 求了。

另一例略显奔放的点乘(出自某场联考的最后一部分):

  • f=idk((μidk)I):取 g=id2k

    h=(idk((μidk)I))(idkidk)=((μidk)idkI)idk=(ϵI)idk=idk

    红色部分是第一例点乘中出现的结果,它等于 ϵ

小结:欲求 f=AC 形的前缀和,我们希望 g 形如 BC,且 g 的前缀和易求。然后,运用上面的等式将 fg 化作 (AB)C。适当地选取 B 可以将 AB 化作简单的函数,从而 fg 的前缀和易求。

简单运用#

例 1#

i=1nj=1mφ(gcd(i,j))

对某个大质数取模

1nm109

经过平凡的推导可以得出原式等于

T=1nnTmTiTμ(i)φ(Ti)

想要整除分块,则需要求出 μφ 的前缀和。

f=μφ,g=I,fg=φ。利用杜教筛求出 φμφ 的前缀和即可。注意到复杂度仍然是 O(n2/3) 的,因为我们可以线性筛预处理 f 的前 O(n2/3) 项。

例 2 (Luogu P3768 简单的数学题)#

i=1nj=1nijgcd(i,j)

对某个大质数取模

1n109

经过平凡的推导可以得出原式等于

d=1nφ(d)d2S(ni)2,where S(x)=x(x+1)2

我们知道了怎么处理点乘,因此杜教筛也是容易的:f=φid2,g=id2,(fg)=id3

例 3#

i=1nj=1nlcm(i,j)k

1n109,1k1000

这碟醋终于出现了!

平凡推导可得原式等于

T=1nSk(nT)2TktTμ(t)tk,where Sk(n)=i=1nik

按照上面点乘处的推导,令 f(n)=nkdnμ(d)dk,请出 g(n)=n2kfg=idk

一个粗劣的复杂度上界是 O((n+n2/3)k),但事实上整个杜教筛过程中也只会用到 O(n)Sk 的值,对它们记忆化可以分析出更好的复杂度 O(nk+n2/3)

[NOI2016] 循环之美#

简要题面:

i=1nj=1m[gcd(i,j)=1][gcd(j,k)=1]

1n,m109,1k103

这题处理技巧比较奇怪,我们不太能直接把两个艾弗森括号都拆成 μ,而是先保留下来 [gcd(j,k)=1] 的那部分。在这里,和一个给定常数互质这个条件是比较好的。

i=1nj=1m[gcd(i,j)=1][gcd(j,k)=1]=j=1m[gcd(j,k)=1]i=1n[gcd(i,j)=1]=j=1m[gcd(j,k)=1]i=1ndgcd(i,j)μ(d)=i=1min(n,m)μ(d)[gcd(d,k)=1]ndj=1m/d[gcd(j,k)=1]

然后要求两个东西:

f(n)=i=1n[gcd(i,k)=1]g(n,k)=i=1n[gcd(i,k)=1]μ(i)

对于 f(n),根据 gcd(i,k)=gcd(i+k,k),艾弗森括号的取值是 k - 循环的,我们只需要预处理 f(1)f(k) 即可。

对于 g(n,k) 则比较需要有想象力。

g(n,k)=i=1nμ(i)dgcd(i,k)μ(d)=1dn,dkμ(d)i=1n/dμ(id)=1dn,dkμ2(d)i=1n/d[gcd(i,d)=1]μ(i)=1dn,dkμ2(d)g(n/d,d)

递归计算,直到递归到 d=1 时用杜教筛求 μ 前缀和。

注意到最终要算 O(n)g(n,...)... 这一维是 k 的因子,因此总复杂度为 O(d(k)n+n2/3+k)

BZOJ3512: DZY Loves Math IV#

(i=1nj=1mφ(ij))mod998244353

1n105,1m109,nm

根据《毒瘤之神的考验》一题快进到

T=1n(i=1n/Tφ(iT)j=1m/Tφ(jT))dTdμ(Td)φ(d)

S(a,b)=i=1aφ(ib),f(n)=dndμ(Td)φ(d),则上式可写作

i=1nf(i)S(n/i,i)S(m/i,i)

在《毒瘤之神的考验》一题中,要用到的全体 S(x,y)O(nlogn) 预处理,从而最终复杂度也是 O(nlogn) 的。

本题需要神秘的观察。我们考虑类似扰动法的策略,

let A(n,m)=i=1nj=1mφ(ij)=i=1nf(i)S(n/i,i)S(m/i,i),thenS(n,k)=i=1nφ(ik)=A(n,k)A(n,k1)=i=1nf(i)S(n/i,i)(S(k/i,i)S((k1)/i,i))=φ(k)ikf(i)S(n/i,i)

注意到 k 这一维是小于等于给定的 n 的(和此处的变量 n 作区分),于是对于 S(m/i,i) 这样的状态来说,计算它们的复杂度不太大。我们可以采用记忆化 + 预处理小的部分来减小递归的复杂度。

Min_25 筛#

综述#

考虑求一类积性函数的前缀和,其中 f 是积性函数,满足:

  • 对于任意素数 pf(p) 处取值是关于 p 的低次多项式。
  • f(pc) 处取值容易求得。

则 Min_25 筛可以在 O(n3/4logn)O(n1ϵ) 的复杂度内求出 f 的前缀和。

流程#

Min_25 筛的求解分为两步:

  1. 对于 W(n)={nx1xn},考虑其中每一个元素 m,求出 1im,iPf(i),即前缀素数点处取值之和;
  2. 根据第一步求得的 O(n) 个答案进而求解出整个前缀和。

第一步#

因为 f(p) 的取值是关于 p 的低次多项式,因此,在这一步中,我们只需要考虑 f(p)=pk 的情形如何求解(对于项数更多的情形,分别求解后求和即可)。

我们通常找一个 f 来替代 f。这样的 f 具有完全积性,且在素数 p 处和 f 相同。这里考虑函数 f(x)=xk

Min_25 筛的第一步,我们要对于每个 m,求出数组 g。其中 g 的定义如下:

g(m,j)=1im,(iPorL(i)>Pj)f(i)

其中 L(i) 表示 i 的最小质因子,Pj 表示第 j 个质数,P0=0。上式即前缀素数点处或最小质因子大于 Pjf 取值之和(注意是 f 而非 f。我们用 f 暂时替代了 f)。

对于 1,其不存在最小质因子,故 [L(1)>Pj] 总是 0。此处可改作 2im,但为了推导方便保留可能的 i=1 取值,虽然 f(1) 永远不能被统计进贡献。

q(m) 表示最大的 j 使得 Pj2m,那么 g(m,q(m)) 即为 1im,iPf(i)

考虑递推求出 g(m,j)。初始有 g(m,0)=i=2mik。对于 j1,我们会在 g(m,j1) 的基础上删除若干个点。质数是不会被删除的,因此我们会恰好删除最小质因子是 Pj非质数点

Pj2>mg(m,j) 不会有变化。

Pj2m,形如 Pjx(Pjxm),且 Pjx 的最小质因子恰好是 Pj 的点会造成贡献。这可以表示为下面的式子:

f(Pj)(g(mPj,j1)g(Pj1,j1))

来解释一下这个式子:首先,我们可以根据完全积性把 f(Pjx) 拆开。现在考虑 x 的取值,很显然是来源于 g(mPj,j1)。但是既小于等于 mPj 又小于等于 Pj1 的质数是不能作为 x 的,因为我们有 Pj1<Pjm,所以 mPjPj1,只需要考虑 g(Pj1,j1) 就可以了。

因此,我们得到了下式:

g(m,j)={g(m,j1)Pj2>mg(m,j1)f(Pj)×(g(mPj,j1)g(Pj1,j1))Pj2m

复杂度分析 · 一#

现在考虑复杂度。根据经典结论 nab=nab,我们对于 mW(n)W(m)W(n)。同时可以证明 1iniW(n),因此对于任意 mW(m) 我们求 g(m,j) 的时候,只会遍历 g 数组中第一维在 W(n) 中的元素。同时,求出单个状态的复杂度是 O(1) 的,因此状态数即为复杂度。

根据范围素数近似估计,复杂度可写作

T(n)=inO(π(i))+O(π(ni))=inO(π(ni))=inO(nilnni)

注意到 in,因此 lnni 只和 lnn 差常数倍。直接将这部分提出来,上面的部分积分,得到

T(n)=O(n3/4logn)

第二步#

综述

现在我们已经知道所有 1im,iPf(i), where mW(n) 了。将其简记为 g(m)。考虑使用这些 g(m) 求出原来的解。

S(m,j) 表示 1im[L(i)>Pj]f(i),即最小质因子大于 Pjf(i) 之和(我们现在抛弃 f 了,同时也不再限定 i 是质数;只不过 1 仍然被抛弃在外)。这样再调用 S(n,0) 就得到答案了。

方法一

这种方法复杂度为 O(n1ϵ),在 n1013 的时候也可以看作 O(n3/4logn),够用。实际测试略快于第二种。这里着重介绍。

仍然考虑如何求解。分 i 是质数或合数两种情况讨论:

  • i 是质数

    根据 g 的定义,这部分的贡献是 g(n)g(Pj)

  • i 是合数

    枚举最小质因子 Pk(k>j),枚举指数 e,利用积性函数的性质进行变量分离,得到

    k>je1,Pkemf(Pke)(S(mPke,k)+[e>1])

    这里 [e>1] 有些耐人寻味。考虑前面的 S(mPke,k) 没有考虑 Pke 自己的贡献。如果 e=1,那么 Pke 就是 Pk,应该归到 i 是质数的分类里,没有贡献。否则,Pke 会造成 f(Pke) 的贡献。

S(n,0)+1 就是答案(我们之前没有考虑 f(1) 的贡献)。

代码 (For Luogu P4213)

略好于之前的版本。

# include <bits/stdc++.h>

const int N=1000010,INF=0x3f3f3f3f,mod=1e9+7,inv6=166666668,inv2=500000004;

typedef long long ll;
int pr[N],pc;
bool vis[N];

int g1[N],g2[N];

ll w[N],idx1[N],idx2[N];

int wtot;

ll MAXN;
ll n;

inline int read(void){
	int res,f=1;
	char c;
	while((c=getchar())<'0'||c>'9')
		if(c=='-')f=-1;
	res=c-48;
	while((c=getchar())>='0'&&c<='9')
		res=res*10+c-48;
	return res*f;
}
inline void init(void){
	for(int i=2;i<=MAXN;++i){
		if(!vis[i]) pr[++pc]=i;
		for(int j=1;i*pr[j]<=MAXN&&j<=pc;++j){
			vis[i*pr[j]]=true;
			if(i%pr[j]==0) break;
		}
	}
	return;
}
inline int sum1(ll x){
	x%=mod;
	return x*(x+1)%mod*inv2%mod;
}
inline int sum2(ll x){
	x%=mod;
	return x*(x+1)%mod*(2*x+1)%mod*inv6%mod;
}
inline int idx(ll x){
	return (x<=MAXN)?idx1[x]:idx2[n/x];
}

inline int adc(int a,int b){
	return (a+b>=mod)?(a+b-mod):(a+b); 
}
inline int dec(int a,int b){
	return (a<b)?(a-b+mod):(a-b);
}
inline void add(int &a,int b){
	a=adc(a,b);
}
inline void del(int &a,int b){
	a=dec(a,b);
}
inline int mul(int a,int b){
	return 1ll*a*b%mod;
}

ll S(ll x,int i){
	if(pr[i]>=x) return 0;
	
	int xid=idx(x),pid=idx(pr[i]);
	ll res=dec(dec(g2[xid],g1[xid]),dec(g2[pid],g1[pid]));
	
	for(int j=i+1;j<=pc&&1ll*pr[j]*pr[j]<=x;++j){
		for(ll e=1,p=pr[j],_p;p<=x;++e,p*=pr[j]){
			_p=p%mod;
			res=adc(res,mul(mul(dec(_p,1),_p),S(x/p,j)+(e>1)));
		}
	}
	return res;
}
int main(void){
	scanf("%lld",&n);
	MAXN=1ll*sqrt(n);
	init();
	for(ll l=1,r;l<=n;l=r+1){
		ll val=n/l;
		r=n/val,w[++wtot]=val;
		g1[wtot]=dec(sum1(val),1),g2[wtot]=dec(sum2(val),1);
		if(val<=MAXN) idx1[val]=wtot;
		else idx2[n/val]=wtot;
	}
	for(int i=1;i<=pc;++i){
		for(int j=1;1ll*pr[i]*pr[i]<=w[j]&&j<=wtot;++j){
			int mid=idx(w[j]/pr[i]),pid=idx(pr[i-1]);
			del(g1[j],mul(pr[i],dec(g1[mid],g1[pid])));
			del(g2[j],mul(mul(pr[i],pr[i]),dec(g2[mid],g2[pid])));
		}
	}
	printf("%d",adc(S(n,0),1));
	return 0;
}

方法二

再度审视 S 的定义:S(m,j) 表示 1im[L(i)>Pj]f(i),即最小质因子大于 Pjf(i) 之和。

我们直接套用第一步中的递推方式。

S(m,j)=S(m,j+1)+[Pj+1m]Pj+1emf(Pj+1e)(S(mPj+1e,j+1)g(min(mPj+1e,Pj+1))+[e>1])

这里的取 min 是不好的。我们注意到当 Pj+1e+1>m 的时候,S()g() 没有贡献,因为这意味着 Pj+1>mPj+1e。于是可以改为

S(m,j)=S(m,j+1)+[Pj+1m]Pj+1e+1mf(Pj+1e)(S(mPj+1e,j+1)g(Pj+1))+f(Pj+1e+1)

使用和第一步相同的方法进行递推即可。

至此可以使用与第一步类似的方法分析出 O(n3/4logn)

方法二的优势是,我们可以求出每个 mW(n) 的前缀 m 的答案。这无疑在整除分块需要计算 O(n) 个前缀的函数值之和的时候给我们提供了便利。

代码 (For LibreOJ 6784)#

# include <bits/stdc++.h>

const int N=2000010,INF=0x3f3f3f3f,mod=1e9+7,inv6=166666668,inv2=500000004;

typedef long long ll;
int pr[N],pc;
bool vis[N];

int g1[N],g0[N];

ll w[N],idx1[N],idx2[N];

int s[N];

int wtot;

ll MAXN;
ll n;

inline int read(void){
	int res,f=1;
	char c;
	while((c=getchar())<'0'||c>'9')
		if(c=='-')f=-1;
	res=c-48;
	while((c=getchar())>='0'&&c<='9')
		res=res*10+c-48;
	return res*f;
}
inline void init(void){
	for(int i=2;i<=MAXN;++i){
		if(!vis[i]) pr[++pc]=i;
		for(int j=1;i*pr[j]<=MAXN&&j<=pc;++j){
			vis[i*pr[j]]=true;
			if(i%pr[j]==0) break;
		}
	}
	return;
}
inline int sum1(ll x){
	x%=mod;
	return x*(x+1)%mod*inv2%mod;
}
inline int sum0(ll x){
	return x%mod;
}
inline int idx(ll x){
	return (x<=MAXN)?idx1[x]:idx2[n/x];
}

inline int adc(int a,int b){
	return (a+b>=mod)?(a+b-mod):(a+b); 
}
inline int dec(int a,int b){
	return (a<b)?(a-b+mod):(a-b);
}
inline void add(int &a,int b){
	a=adc(a,b);
}
inline void del(int &a,int b){
	a=dec(a,b);
}
inline int mul(int a,int b){
	return 1ll*a*b%mod;
}
inline int f(int x,int k){
	return x^k;
}

std::vector <int> vec;

int main(void){
	scanf("%lld",&n);
	MAXN=1ll*sqrt(n);
	init();
	for(ll l=1,r;l<=n;l=r+1){
		ll val=n/l;
		r=n/val,w[++wtot]=val;
		g1[wtot]=dec(sum1(val),1),g0[wtot]=dec(sum0(val),1);
		if(val<=MAXN) idx1[val]=wtot;
		else idx2[n/val]=wtot;
	}
	for(int i=1;i<=pc;++i){
		for(int j=1;1ll*pr[i]*pr[i]<=w[j]&&j<=wtot;++j){
			int mid=idx(w[j]/pr[i]),pid=idx(pr[i-1]);
			del(g1[j],mul(pr[i],dec(g1[mid],g1[pid])));
			del(g0[j],dec(g0[mid],g0[pid]));
		}
	}
	
	for(int i=1;i<=wtot;++i) s[i]=g1[i]=adc(dec(g1[i],g0[i]),(w[i]>=2)*2);
	for(int i=pc;i;--i){
		for(int j=1;1ll*pr[i]*pr[i]<=w[j]&&j<=wtot;++j){
			for(ll e=1,p=pr[i];p<=w[j]/pr[i];++e,p*=pr[i]){
				add(s[j],mul(f(pr[i],e),dec(s[idx(w[j]/p)],g1[idx(pr[i])])));
				add(s[j],f(pr[i],e+1));
			}
		}
	}
	vec.resize(wtot);
	for(int i=0;i<wtot;++i) vec[i]=adc(s[i+1],1);
	std::sort(vec.begin(),vec.end());
	vec.erase(std::unique(vec.begin(),vec.end()),vec.end());
	int ans=0;
	for(auto v:vec) ans^=v;
	printf("%d",ans);
	
	return 0;
}

小试牛刀#

例 1#

(杜教筛 例 3)

i=1nj=1nlcm(i,j)k

1n109,1k1000

T=1nSk(nT)2TktTμ(t)tk

快进到这里。

那么我们只要瞪眼法看出后面是积性函数,直接上 Min_25 筛就完事了,不需要观察到可以卷 id2k

Typora 崩掉了,不写了。

作者:Meatherm

出处:https://www.cnblogs.com/Meatherm/p/18314262

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   Meatherm  阅读(70)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示