莫比乌斯反演

莫比乌斯反演

引例:[HAOI2011]Problem b

  • 题目描述

对于给出的T个询问,每次求有多少个数对(x,y)满足 a ≤ x ≤ b ,c ≤y ≤ d,且gcd(x,y)=k?

  • 输入格式

    第一行为一个整数T,接下来T行每行五个整数,分别表示a,b,c,d,k。

  • 输出格式

    共T行,每行一个整数表示满足要求的数对(x,y)的个数

  • 样例输入 样例输出

    2

    2 5 1 5 1 14

    1 5 1 5 2 3

  • 数据范围

    20%的数据满足:1 ≤T,k ≤100,1 ≤ a ≤ b ≤ 100,1 ≤ c ≤ d ≤ 100。

    50%的数据满足:1 ≤T,k ≤1000,1 ≤ a ≤ b ≤ 1000,1 ≤ c ≤ d ≤ 1000。

    70%的数据满足:1 ≤T ≤1000,1 ≤ a ≤ b ≤ 10000,1 ≤ c ≤ d ≤ 10000,1 ≤k ≤10000。

    100%的数据满足:1 ≤T,k ≤50000,1 ≤ a ≤ b ≤ 50000,1 ≤ c ≤ d ≤ 50000。

  • 算法一

  • 枚举[a,b]中的每一个数x,枚举[c,d]中的每一个数y,判断gcd(x,y)是否等于k,单次询问的时间复杂度是O(Tnm\(lnn\))。

    当然,我们可以对上述暴力的算法进行优化,令x=k×x1,y=k×y1,则\(\lceil\frac{a}{k}\rceil\)≤x1≤\(\lceil\frac{b}{k}\rceil\)\(\lceil\frac{c}{k}\rceil\)≤x1≤\(\lceil\frac{d}{k}\rceil\),枚举x1,y1,判断gcd(x1,y1)是否等于1即可,时间复杂度为O(T\(\frac{nmln\frac{n}{k}}{k^2}\)),预计得分20。

  • 算法二

    设f(n,m)表示有多少个数对(x,y)满足1≤x≤n,1≤y≤m,且gcd(x,y)=k,则原问题的解=f(b,d)-f(a-1,d)-f(b,c-1)+f(a-1,c-1),计算f(n,m)的时候,我们先枚举1到\(\lfloor\frac{n}{k}\rfloor\)的每一个数x,再枚举1到\(\lfloor\frac{m}{k}\rfloor\)的每一个数y,判断gcd(x,y)=1,公式为f(n,m) = \(\sum_{x=1}^{\frac{n}{k}}\sum_{y=1}^{\frac{m}{k}}{gcd(x,y)=1}\)

    设g(x)表示1到\(\lfloor\frac{m}{k}\rfloor\)中与x互质的数的个数,则f(n,m)=\(\sum_{x=1}^{\frac{n}{k}}{g(x)}\),如果能求出来g(x),那么就会省去第二层的枚举。如何求g(x)呢?

    根据算数唯一分解定理,设x = \(p_1^{a_1}p_2^{a_2}p_3^{a_3}……p_t^{a_t}\),假设A集合表示1到\(\lfloor\frac{m}{k}\rfloor\)中含\(p_i\)的集合,利用容斥原理:

    g(x)=\(\lfloor\frac{m}{k}\rfloor\)—|\(\cup_{i=1}^{t}{A_i}\)|=\(\lfloor\frac{m}{k}\rfloor\)\(\sum_{i=1}^{t}{|A_i|}\) + \(\sum_{i,j=1}^{t}{|A_i\cap{A_j}|}\)\(\sum_{i,j,k=1}^{t}{|A_i\cap{A_j}\cap{A_k}|}\)+……

    假设x=30,\(\lfloor\frac{m}{k}\rfloor\)=100,\(|A_i|\)={2,3,5}

    g(x)=100—\(\lfloor\frac{100}{2}\rfloor\)\(\lfloor\frac{100}{3}\rfloor\)\(\lfloor\frac{100}{5}\rfloor\)+\(\lfloor\frac{100}{2*2}\rfloor\)+\(\lfloor\frac{100}{2*5}\rfloor\)+\(\lfloor\frac{100}{3*5}\rfloor\)\(\lfloor\frac{100}{2*3*5}\rfloor\)=26,这样的话,计算g(x)的时间复杂度为O(\(2^{t(x)}\))(枚举子集的容斥的时间复杂度,如果是本质不同的数集,则时间复杂度为\(2^n\),n为不同的数字的个数),t(x)为x的质因子的个数,对于50%的数字,n<=1000,按照最小的质因子乘起来,\(2*3*5*7*11\)=2310 > 1000,所里这里的t(x)最多为4,所以整体的时间复杂度为O(T\(\lfloor\frac{n}{k}\rfloor*\sum_{}2^{t(x)}\)),只能过50%的数据

  • 算法三

    设f(k)表示有多少个数对(x,y)满足1≤x≤n,1≤y≤m,且gcd(x,y)=k,在这里,我们设定n≤m

    设g(k)表示1≤x≤n,1≤y≤m,且k|gcd(x,y)的数对的个数,这里gcd(x,y)是k的倍数,例如gcd(x,y)=k,2k,3k,4k……\({\lfloor\frac{n}{k}\rfloor} *k\),所以g(k)=\(\sum_{d=1}^{\lfloor\frac{n}{k}\rfloor}f(k*d)\) ,g(k)的设定非常重要

    g(k)的求值简单,找到1—n中k的倍数x,找到1—m中k的倍数y,gcd(x,y)一定是k的倍数,那么1—n中k的倍数有多少个呢?\({\lfloor\frac{n}{k}\rfloor}\)个,同理,1—m中k的倍数有\({\frac{m}{k}}\)个,根据乘法原理,g(k)=\({\lfloor\frac{n}{k}\rfloor}\)*\(\lfloor{\frac{m}{k}}\rfloor\)

    举例说明:k=2,n=4,m=6

    n里面2的倍数有2个{2,4},m里面2的倍数有3个{2, 4, 6}

    {2,2}、{2,4}、{4,2}、{4,4}、

  • 对于70%和100%的数据,无法解决

引入:莫比乌斯函数

  • 定义f(n)和g(n)是定义再正整数集合上的两个函数,满足以下的关系

    f(n)=\(\sum_{d|n}{g{(d)}}\)

    根据定义,我们可以知道: 根据f(n)推导出g(n):

    f(1)=g(1) g(1)=f(1)

    f(2)=g(1)+g(2) g(2)=f(2) - f(1)

    f(3)=g(1)+g(3) g(3)=f(3) - f(1)

    f(4)=g(1)+g(2)+g(4) g(4)=f(4) - g(2) -g(1)=f(4) -f(2)

    f(5)=g(1)+g(5) g(5)=f(5) - f(1)

    f(6)=g(1)+g(2)+g(3)+g(6) g(6)=f(6) -f(2) -f(3) +f(1)

    f(7)=g(1)+g(7) g(7)=f(7) - f(1)

    f(8)=g(1)+g(2)+g(4)+g(8) g(8)=f(8) - f(4)

    f(9)=g(1)+g(3)+g(9) g(9)=f(9) - f(3)

    f(10)=g(1)+g(2)+g(5)+g(10) g(10)=f(10) - f(5) - f(2) +f(1)

    f(11)=g(1)+g(11) g(11)=f(11) - f(1)

    f(12)=g(1)+g(2)+g(3)+g(4)+g(6)+g(12) g(12)=f(12) - f(6) -f(4) +f(2)

    我们观察发现:

    g(n)=\(\sum_{d|n}{f(d)*?}\)

    问号( ? )部分与d无关,与\(\frac{n}{d}\)有关,我们设这部分的值为\(\mu\)(\(\frac{n}{d}\)),我们来猜测一下\(\mu\)(\(\frac{n}{d}\))的值:

    观察g(2)的值,我们猜测

    \(\mu(1)\)= 1 , \(\mu(2)\)= -1, \(\mu(3)\)= -1,\(\mu(4)\)= 0,\(\mu(5)\)= -1,\(\mu(6)\)= 1,

    \(\mu(7)\)= -1,\(\mu(8)\)= 0,\(\mu(9)\)= 0,\(\mu(10)\)= 1,\(\mu(11)\)= -1,\(\mu(12)\)= 0

    \(\mu(d)\)为莫比乌斯函数,定义如下:

    若d=1,\(\mu(1)\)= 1;

    若d= \(p_1^{a_1}p_2^{a_2}p_3^{a_3}……p_t^{a_t}\),只要有一个质数的指数大于1,则\(\mu(d)\)=0;

    若d= \(p_1p_2p_3……p_t\),则\(\mu(d)\)=\((-1)^t\)

莫比乌斯函数的性质

  • 性质一:对于任意正整数n,有\(\sum_{d|n}{\mu{(d)}}=\begin{cases}1, \quad if\quad n=1\\0,\quad if\quad n\neq0\\ \end{cases}\)

证明方法一

  1. n=1 时,\(\sum_{d|n}{\mu{(d)}}\)=1;
  2. n>1时,设n=\(p_1^{x_1}p_2^{x_2}p_3^{x_3}……p_k^{x_k}\),设约数d=\(p_1^{y_1}p_2^{y_2}p_3^{y_3}……p_k^{y_k}\),如果\(y_i\)的值大于等于2,则\(\mu\)的值为0,所以我们只考虑\(y_i\)为0或者为1的情况,假设约数d中有r个互异的质因子,质因子的指数为1,则对应的\(\mu(d)\)=\((-1)^r\),这样的约数d有\(C_k^r\)个,所以\(\sum_{d|n}{\mu{(d)}}=\sum_{r=0}^{k}C_k^r(-1)^r*1^{k-r}\)=\((1-1)^k\)=0。

上面式子可以进行重要替换,将n替换成gcd(x,y),

\[\sum_{d|gcd(x,y)}{\mu{(d)}}=\begin{cases}1, \quad if\quad gcd(x,y)=1\\0,\quad if\quad gcd(x,y)\neq0\\ \end{cases} \]

  • 性质二: 莫比乌斯函数\(\mu{(n)}\)是积性函数

证明:

  1. n=1时,\(\mu{(1)}\)=1;

  2. 如果a和b互质,我们很容易证明\(\mu{(ab)}=\mu{(a)}*\mu{(b)}\)。分以下几种情况讨论:

    假设a或者b中有一个的指数大于等于2,则\(\mu{(a)}\)=0或者\(\mu{(b)}\)=0,\(\mu{(ab)}\)=0;

    假设a或者b中的质因子都等于1,因为a和b互质,则易证\(\mu{(ab)}=\mu{(a)}*\mu{(b)}\)

  • 如何求莫比乌斯函数

由于莫比乌斯函数是积性函数,所以我们可以在线性筛中求函数值

void seive(int n){
	mu[1]=1;
	for(int i=2;i<=n;i++){
		if(vis[i]==0){
			pri[++cnt]=i;
			mu[i]=-1;//说明这是一个质数 
		}
		for(int j=1;j<=cnt;j++){
			int t=i*pri[j];//即将被筛掉的数字 
			if(t>n) break;//线性筛的判断条件
            vis[t]=1;
			if(i%pri[j]==0) break;//说明pri[j]的指数大于等于2
			else mu[t]=-mu[i];//增加了一个质因子pri[j],所以变成-1 
		}
	}
}

常用的数论函数

我们不必熟悉整个数论函数的体系,我们只需要了解一些常见数论函数即可:

  1. \(\phi(n)\) 欧拉函数 表示1~n中与n互质的数的个数
  2. \(μ(n)\) 莫比乌斯函数
  3. \(d(n)\) 约数个数 表示n的正因子个数
  4. \(σ(n)\) 约数和函数 表示n的所有正因子之和
  5. \(ϵ(n)\) 元函数 定义\(ϵ(n)=[n==1]\)
  6. \(1(n)\) 恒等函数 定义:\(1(n)=1\)
  7. \(Id(n)\) 单位函数 定义:\(Id(n)=n\)
  8. \(I_k(n)\) 单位函数 定义:\(I_k(n)=n^k\)

如果一个数论函数 f ,对于任意两个的正互质的正整数a,b,如果满足\(f(a×b)=f(a)×f(b)\),我们把函数 f 称作积性函数,注意积性函数与积性函数的乘积仍为积性函数。上述数论函数都是积性函数;

若函数f对于任意一个正整数对(a,b)都满足\(f(a×b)=f(a)×f(b)\),那么我们把函数f称作完全积性函数。上面的5,6,7,8都是完全积性函数。

狄利克雷卷积

狄利克雷卷积:定义一种运算∗,对于函数f,g满足

\[(f∗g)(n)=\sum_{d|n}f(d)g(\frac{n}{d}) \]

​,我们把这种运算,称作狄利克雷卷积。狄利克雷卷积满足交换律,结合律以及分配律。元函数\(ϵ(n)\)是狄利克雷卷积的乘法单位,元函数可以在狄利克雷卷积中充当单位元,即\(f∗ϵ=f\)(f函数卷上单位函数,等于f函数)。

交换律:\(f*g=g*f\)

结合律:\(f*(g*h)=(f*g)*h\)

分配律:\(f*(g+h)=f*g+f*h\)

常用的数论函数卷积

  1. \(\mu*1=\epsilon\)

    \[(\mu*1)(n)=\sum_{d|n}{\mu(d)}*1(\frac{n}{d})=\sum_{d|n}{\mu(d)}=\begin{cases}1, \quad if\quad n=1\\0,\quad if\quad n\neq0\\ \end{cases} \quad=\quad[n==1]=\epsilon \]

  2. \(\phi*1=I_k\)

    \[n=\prod_{i=1}^{k}{p_i^{x_i}},因为\phi函数为积性函数,只需要证明n^,=p_i^{x_i}时\phi*1=id即可\\ \phi*1=\sum_{d|n^,}{\phi(d)}=\sum_{i=0}^{k}{\phi(p_i^{x_i})}\\ =1+(p_i-1)+p_i*(p_i-1)+p_i^2*(p_i-1)+……+p_i^{x_i-1}*(p_i-1)\\ =p_i^{x_i}=I_k \]

莫比乌斯反演

莫比乌斯反演的公式如下

\[f(n)=\sum_{d|n}{g(d)}\longrightarrow g(n)=\sum_{d|n}f(\frac{n}{d})*\mu{(d)}\\ 另一种形式\\ f(n)=\sum_{d|n}{g(d)}\longrightarrow g(n)=\sum_{d|n}f(d)*\mu{(\frac{n}{d})} \]

证明方法一:在这里先将f函数替换成g函数

\[\sum_{d|n}f(\frac{n}{d})*\mu{(d)}=\sum_{d|n}{\mu{(d)}\sum_{i|\frac{n}{d}}{g(i)}} \]

接下来考虑以g(i)为主体的等价形式,如何变化呢?

\[\sum_{d|n}{\mu{(d)}\sum_{i|\frac{n}{d}}{g(i)}}=\sum_{i|n}{g(i)}\sum_{d|{\frac{n}{i}}}{\mu{(d)}} \]

第一步为什么可以变到第二步?

两步之间的转换必须具备两个条件:条件1是把n分成两部分,是一部分是x,一部分是\(\frac{n}{x}\)部分,条件2是这两部分分别属于两个数论函数,且两个函数都需求和,所以无论形式如何,只要满足这两个条件即可。

举个例子,当n=6时

\[式子1\\ \mu(1)*(g(1)+g(2)+g(3)+g(6))\\ \mu(2)*(g(1)+g(3))\\ \mu(3)*(g(1)+g(2))\\ \mu(6)*g(1)\\ 式子2\\ g(1)*(\mu(1)+\mu(2)+\mu(3)+\mu(6))\\ g(2)*(\mu(1)+\mu(3))\\ g(3)*(\mu(1)+\mu(2))\\ g(6)*\mu(1)\\ 式子1和式子2相同,当然,下面的几个式子都是相同的\\ \sum_{d|n}{\mu{(d)}\sum_{i|\frac{n}{d}}{g(i)}}=\sum_{i|n}{g(i)}\sum_{d|{\frac{n}{i}}}{\mu{(d)}}=\sum_{i|n}{g(i)}\sum_{d|n}{\mu(\frac{n}{d})}=\sum_{k|n}\sum_{d|\frac{n}{k}}g(\frac{n}{kd}){\mu(d)} \]

接下来我们考虑\(\sum_{d|\frac{n}{i}}{\mu{(d)}}\)(主要利用\(\mu{}\)函数的性质一):

  1. 如果i=n,\(\sum_{d|\frac{n}{i}}{\mu{(d)}}=\mu{(1)}=1\),所以\(\sum_{}{g(i)}\sum_{d|\frac{n}{i}}{\mu{(d)}}=g(n)\)
  2. 如果i取小于n的约数,\(\sum_{d|\frac{n}{i}}{\mu{(d)}}=0\)\(\sum_{}{g(i)}\sum_{d|\frac{n}{i}}{\mu{(d)}}\)=0

综上,\(\sum_{d|n}f(\frac{n}{d})*\mu{(d)}=g(n)\),得证!

证明方法二:

使用狄利克雷卷积证明:

\[\sum_{d|n}f(\frac{n}{d})*\mu{(d)},这样的形式是\mu*f,根据交换律,\mu*f=f*\mu\\ f*\mu=g*1*\mu\\ 因为1*\mu=\epsilon,所以\\ f*\mu=g*\epsilon=g,得证! \]

莫比乌斯反演的变形

  • 变形一

\[f(i)=\sum_{k=1}^{\lfloor\frac{n}{i}\rfloor}g(k*i)(k是倍数) \longrightarrow g(i)=\sum_{k=1}^{\lfloor\frac{n}{i}\rfloor}f(k*i)\mu{(k)} \]

根据f(i)的定义,进行下列式子的变化

\[我们将k*i看成一个未知数,根据f(i)的定义\\ \sum_{k=1}^{\lfloor\frac{n}{i}\rfloor}f(k*i)\mu{(k)}=\sum_{k=1}^{\lfloor\frac{n}{i}\rfloor}{\mu{(k)}}\sum_{k_1=1}^{\lfloor\frac{n}{i*k}\rfloor}g(k_1*k*i) \]

上式中我们需要枚举k,枚举\(k_1\)接下来的变化很关键,设T=\(k_1\)*k,那么T的范围是多少呢?考虑两个边界

  1. 当k=1,i的值为n,\(k_1\)为1,T的值为1
  2. 当k=n,i的值为1,\(k_1\)为1,T的值为n

所以T的取值范围为[1,n]。

则k是T的约数,上面式子可以进行如下变化:

\[\sum_{k=1}^{\lfloor\frac{n}{i}\rfloor}{\mu{(k)}}\sum_{k_1=1}^{\lfloor\frac{n}{i*k}\rfloor}g(k_1*k*i)=\sum_{T=1}^{n}{g(T*i)}\sum_{k|T}{\mu{(k)}} \]

根据莫比乌斯函数的性质一,可得:

\[如果T=1,则\sum_{k|T}{\mu{(k)}}=1,\\ \sum_{T=1}^{n}{g(T*i)}=\sum_{T=1}^{n}{g(i)}\\ 如果T>1,则\sum_{k|T}{\mu{(k)}}=0,\\ \sum_{T=1}^{n}{g(T*i)}=0\\ 结合两种情况所述,\sum_{k=1}^{\lfloor\frac{n}{i}\rfloor}f(k*i)\mu{(k)}=\sum_{T=1}^{n}{g(T*i)}\sum_{k|T}{\mu{(k)}}=g(i),原式得证。 \]

应用

回想引例中的问题,我们令g(i)表示 x\(\in\)[1, n],y\(\in\)[1,m],gcd(x,y)=i的数对的个数

令f(i)表示 x\(\in\)[1, n],y\(\in\)[1,m],且i|gcd(x,y)的数对的个数,那么f(i)和g(i)正好满足变形一中的式子关系。

所以变形一适用于g(i)很难求,但是\(\sum_{k=1}^{\lfloor\frac{n}{i}\rfloor}g(k*i)\)很好求,可以利用反演来计算g(i)

  • 变形二

\[f(i)=\sum_{i|d,d \leqslant n}{g(d)}(d是i的倍数)\longrightarrow g(i)=\sum_{i|d,d \leqslant n}{f(d)}{\mu(\frac{d}{i})} \\ \]

变形二和变形一的意思其实是一样的。

莫比乌斯反演的性质

性质:

f(n)和g(n)都是定义在正整数集合上的两个函数,并且满足\(f(n)=\sum_{d|n}{g(d)}\)

g(n)是积性函数的充分必要条件是f(n)是积性函数

充分性:\(f(n)\)是积性函数\(\longrightarrow\ g(n)\)是积性函数

设gcd(a,b)=1,\(a=\prod{p_i}^{x_i},b=\prod{q_i}^{y_i}\),根据莫比乌斯反演可得,\(g(n)=\sum_{d|n}{f(\frac{n}{d})}{\mu(d)}\)

\(g(a)=\sum_{d_1|a}{f(\frac{a}{d_1})}{\mu(d_1)},g(b)=\sum_{d_2|b}{f(\frac{b}{d_2})}{\mu(d_2)},g(a)g(b)=\sum_{d_1|a}{f(\frac{a}{d_1})}{\mu(d_1)}\sum_{d_2|b}{f(\frac{b}{d_2})}{\mu(d_2)}\),

其中gcd(\(d_1,d_2\))=1,gcd(\(\frac{a}{d_1},\frac{b}{d_2}\))=1,同时\(d_1|a, d_2|b, d_1d_2|ab\)

因为f和\(\mu\)函数都是积性函数,所以

\[\sum_{d_1|a}{f(\frac{a}{d_1})}{\mu(d_1)}\sum_{d_2|b}{f(\frac{b}{d_2})}{\mu(d_2)}=\sum_{(d_1d_2)|ab}{f(\frac{a}{d_1})}{f(\frac{b}{d_2})}{\mu(d_1)}{\mu(d_2)}=\sum_{(d_1d_2)|ab}{f(\frac{ab}{d_1d_2})}{\mu(d_1d_2)}=g(ab) \]

必要性:\(g(n)\)是积性函数\(\longrightarrow\ f(n)\)是积性函数

设gcd(a,b)=1,\(d_1|a,d_2|b\), 那么

\[f(a)=\sum_{d_1|a}g(d_1)\\ f(b)=\sum_{d_2|b}g(d_2)\\ gcd(d_1,d_2)=1,同时d_1|a, d_2|b, d_1d_2|ab,\\ f(a)f(b)=\sum_{d_1|a}g(d_1)\sum_{d_2|b}g(d_2)=\sum_{(d_1d_2)|ab}g(d_1d_2)=g(ab) \]

引例的算法四

70%的数据满足:1 ≤T ≤1000,1 ≤ a ≤ b ≤ 10000,1 ≤ c ≤ d ≤ 10000,1 ≤k ≤10000。

100%的数据满足:1 ≤T,k ≤50000,1 ≤ a ≤ b ≤ 50000,1 ≤ c ≤ d ≤ 50000。

设f(k)表示有多少个数对(x,y)满足1≤x≤n,1≤y≤m,且gcd(x,y)=k,在这里,我们设定n≤m

设g(k)表示1≤x≤n,1≤y≤m,且k|gcd(x,y)的数对的个数,这里gcd(x,y)是k的倍数,例如gcd(x,y)=k,2k,3k,4k……\({\lfloor\frac{n}{k}\rfloor} *k\),所以g(k)=\(\sum_{d=1}^{\lfloor\frac{n}{k}\rfloor}f(k*d)\) ,g(k)的设定非常重要

根据g(k)和f(k)的关系,完全符合莫比乌斯变形一的式子,所以

\[f(k)=\sum_{d=1}^{\lfloor\frac{n}{k}\rfloor}g(kd)\mu{(d)},其中d为系数\\ g(kd)=\lfloor\frac{n}{kd}\rfloor*\lfloor\frac{m}{kd}\rfloor\\ f(k)=\sum_{d=1}^{\lfloor\frac{n}{k}\rfloor}\lfloor\frac{n}{kd}\rfloor*\lfloor\frac{m}{kd}\rfloor*\mu{(d)} \]

\(\mu(d)\)的值,我们可以在线性筛中O(n)中预处理出来,这样单词询问的时间复杂度为\(O(\frac{n}{k})\),总的时间复杂度为\(O(T*\frac{n}{k})\),可以得到70%的分数

引例的算法五

观察\(f(k)\)的式子,以n=32,m=40,k=2为例,观察\(\lfloor\frac{n}{kd}\rfloor*\lfloor\frac{m}{kd}\rfloor\)的变化:

zcfk.png

如上图所示,蓝色部分为\(\lfloor\frac{n}{kd}\rfloor\)相同的部分,绿色部分为\(\lfloor\frac{m}{kd}\rfloor\)相同的部分,这部分的内容为整除分块的知识点

整除分块

对于\(f(d)=\lfloor\frac{n}{d}\rfloor\)这样的式子,我们需要计算f(d)的值,暴力枚举d的话,时间复杂度为\(O(n)\)

但正如上图中看到的,f(d)的值呈现中一段一段的连续形态,如果我们能找到每一段的左端点和右端点的话,只需要枚举每一段的左右端点即可,不需要全部枚举,那么对于每一段来说,左右端点如何寻找呢?假设左右端点为\(l,r\)

显然,\(l=r+1\),那么r是多少呢?\(r=\lfloor\frac{n}{\lfloor\frac{n}{l}\rfloor}\rfloor\),证明如下

\[假设一段内的i,则\lfloor\frac{n}{i}\rfloor=\lfloor\frac{n}{l}\rfloor\\ 因为\frac{n}{i}>=\lfloor\frac{n}{i}\rfloor\\ 所以\frac{n}{\frac{n}{i}}<=\frac{n}{\lfloor\frac{n}{i}\rfloor} \longrightarrow i<=\frac{n}{\lfloor\frac{n}{i}\rfloor},替换掉\lfloor\frac{n}{i}\rfloor\\ i<=\frac{n}{\lfloor\frac{n}{l}\rfloor}\\ 所以最大的i,即右端点为\frac{n}{\lfloor\frac{n}{l}\rfloor} \]

for(int l=1,r;l<=n;l=r+1){
		r=n/(n/l);//找到右边界 
		……
	} 

时间复杂度是多少呢?\(O(\sqrt{n})\),证明如下:

  1. \(1 \leq d \leq \sqrt{n}时,\lfloor\frac{n}{d}\rfloor最多有\sqrt{n}个不同的取值\)
  2. \(\sqrt{n}+1 \leq d \leq n时,由于\lfloor\frac{n}{d}\rfloor \leq \lfloor\sqrt{n}\rfloor,\lfloor\frac{n}{d}\rfloor最多有\sqrt{n}个不同的取值\)

引例中的算法五继续

观察\(f(k)\)的式子,以n=32,m=40,k=2为例,观察\(\lfloor\frac{n}{kd}\rfloor*\lfloor\frac{m}{kd}\rfloor\)的变化:

zcfk.png

如上图所示,蓝色部分为\(\lfloor\frac{n}{kd}\rfloor\)相同的部分,绿色部分为\(\lfloor\frac{m}{kd}\rfloor\)相同的部分,红色部分为\(\lfloor\frac{n}{kd}\rfloor和\lfloor\frac{m}{kd}\rfloor\)相同的部分,按照整除分块的知识来处理这个这些式子(其中k是定值),相同的部分的值是定值,但是\(\mu\)值不一样,我们可以在线性筛中预处理出\(\mu\)函数的前缀和,接下来在\(O(\sqrt{\frac{n}{k}})\)内处理单次询问。100分可以实现。

代码如下:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int t,k,a,b,c,d,cnt;
ll pri[50005],vis[50005],mu[50005],sum[50005];
//solve其实求的是min([1,x],[1,y])之间的数字
void seive(int n){
	mu[1]=1;
	for(int i=2;i<=n;i++){
		if(vis[i]==0){
			pri[++cnt]=i;
			vis[i]=i;
			mu[i]=-1;//说明这是一个质数 
		}
		for(int j=1;j<=cnt;j++){
			int t=i*pri[j];//即将被筛掉的数字 
			if(t>n) break;//线性筛的判断条件 
			vis[t]=pri[j];//能够筛掉t的最小质因子是pri[j];
			if(i%pri[j]==0) break;
			else mu[t]=-mu[i];//增加了一个质因子pri[j],所以变成-1 
		}
		//不能在这里求前缀和,因为缺少mu[1] 
	}
	for(int i=1;i<=n;i++)  sum[i]=sum[i-1]+mu[i];//求莫比乌斯函数的前缀和 
}
ll solve(int x,int y,int k){
	ll ans=0,r1,r2,r;
	int rj=min(x,y);
	//所以,l到r,其实枚举的是d的不同赋值 
	for(int l=1;l<=rj;l=r+1){
		r1=x/(x/l);//找到第一个右边界
		r2=y/(y/l);;//找到第二个右边界
		r=min(r1,r2);//是k*d使得他们呈现出分块的性质,k是一个定值,所以其实是d使得它呈现出分块的性质 
		ans+=(x*1ll/(1ll*l*k))*(y*1ll/(1ll*l*k))*(sum[r]-sum[l-1]); 
	} 
	return ans;
}
int main(){
	scanf("%d",&t);
	seive(50005);
	
	for(int i=1;i<=t;i++){
		scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
		ll ans=solve(b,d,k)-solve(a-1,d,k)-solve(b,c-1,k)+solve(a-1,c-1,k);
		printf("%lld\n",ans);
	}
	return 0;
}

例2:YY的GCD

题目描述:神犇 YY 虐完数论后给傻× kAc 出了一题,给定 N, M求 \(1 \leq x \leq N,1 \leq y\leq M\)且 $\gcd(x,y) $为质数的 (x, y)有多少对?

输入格式:第一行一个整数 T表述数据组数,接下来 T行,每行两个正整数N, M。

输出格式:T行,每行一个整数表示第 i 组数据的结果。

输入: 输出:

2
10 10                                   30
100 100                                 2791

数据:\(T=10^4,N, M \leq 10^7\)

算法一:

\[f(p)=\sum_{x=1}^{n}\sum_{y=1}^{m}{[ gcd(x,y)=p]} (p\in prime)\\ ans=\sum_{p\in prime}{f(p)}\\ 求解f(p)函数的过程和上一题类似\\ 在O(\sum_{p \in prime}\sqrt{\frac{n}{p}})内处理单次询问\\ n里面有多少个p呢?\frac{n}{\ln n} \\ 所以这样的时间复杂度会超时 \]

算法二:

\[ans=\sum_{p\in prime}{f(p)}=\sum_{p\in prime}\sum_{d=1}^{\min({\frac{n}{p}},{\frac{m}{p}})}{\frac{n}{pd}*\frac{m}{pd}*\mu{(d)}}\\ 此时,我们需要枚举p,d,超时\\ 设T=pd,则T \in [1,n](T的范围在莫比乌斯变形一中讨论过),假设n<m\\ ans=\sum_{T=1}^{n}{\frac{n}{T}*\frac{m}{{T}}*\mu{(\frac{T}{p})}}\\ 又回到了整除分块的内容,现在的问题是如何求\sum_{T=1}^{n}{\mu{(\frac{T}{p})}}\\ 思考,在线性筛中我们能求质数和\mu 函数值,求约数的话,我们可以转换成倍数问题\\ 所以我们枚举素数,以及素数的倍数,求前缀和即可\\ 时间复杂度分析:因为\sum_{i=1}^{n}{\frac{n}{i}}=n\ln{n},均摊到每个素数的时间为\ln n,共有\frac{n}{\ln n}个素数\\ 所以处理线性筛和前缀和的时间复杂度为o(n),总的时间复杂度为o(n+t*\sqrt{n})\\ 代码如下 \]

#include<bits/stdc++.h>
using namespace std;
const int maxn=10000005;
typedef long long ll;
int t,mu[maxn],pri[1000005],sum[maxn],n,m,vis[maxn],cnt,f[maxn];
void seive(int x){
	mu[1]=1;
	for(int i=2;i<=x;i++){
		if(!vis[i]){
			pri[++cnt]=i;
			vis[i]=i;
			mu[i]=-1;
		}
		for(int j=1;j<=cnt;j++){
			int tt=i*pri[j];
			if(tt>x||vis[i]<pri[j]) break;
			vis[tt]=pri[j];
			if(i%pri[j]==0) mu[tt]=0;
			else mu[tt]=-mu[i];
		}
	}
	for(int j=1;j<=cnt;j++)
	//这里是枚举倍数 
		for(int k=1;k*pri[j]<=x;k++){
			f[k*pri[j]]+=mu[k];// 
		}
	for(int i=1;i<=x;i++) sum[i]=sum[i-1]+f[i];
}
ll solve(int x,int y){
	ll ans=0;
	for(int l=1,r=0;l<=x;l=r+1){
		r=min(x/(x/l),y/(y/l));
		ans+=((1ll*x)/l)*((1ll*y)/l)*(1ll*(sum[r]-sum[l-1]));
	}
	return ans;
}
int main(){
	scanf("%d",&t);
	seive(10000000);
	for(int i=1;i<=t;i++){
		scanf("%d%d",&n,&m);
		if(n>m) swap(n,m);
		ll ans=solve(n,m);
		printf("%lld\n",ans);
	}
}

算法三:

\[ans=\sum_{T=1}^{n}{\frac{n}{T}*\frac{m}{{T}}*\mu{(\frac{T}{p})}}\\ 假设g(T)=\sum_{T=1}^{n}{\mu(\frac{T}{p})},如何求g(T)函数呢?\\ 由于\mu函数的特殊性(指数\geq 2则为0),设T=\prod_{i=1}^{k}{p_i^{x_i}}\\ 假设对于某个质因子p_i\\ 如果max(x_i) \geq3,g(T)=0\\ 如果有两个不同的质因子的指数都为2,则g(T)=0\\ 如果只有一个质因子的指数为2,则g(T)=(-1)^k\\ 如果全部质因子都为1,则g(T)=k*(-1)^{k-1}\\ 时间复杂度为O(n)\\ 那么如何维护质因子指数呢?\\ \]

#include<bits/stdc++.h>
using namespace std;
const int maxn=10000005;
typedef long long ll;
int t,pri[1000005],sum[maxn],n,m,vis[maxn],cnt,f[maxn];
int zs[maxn],tc[maxn];
bool flag[maxn];
//其中zs表示i里面不同的质因子的个数,tc表示i里面含有指数为2的质因子的个数

int js(int x){
	int ans=1;
	for(int i=1;i<=zs[x];i++){
		ans=ans*(-1);
	}
	return ans;
}
int js1(int x){
	int ans=0,k=1;
	for(int i=1;i<=zs[x];i++){
		 ans++;
	}
	for(int i=1;i<=(zs[x]-1);i++){
		k=k*(-1);
	}
	return ans*k;
} 
void seive(int x){
	for(int i=2;i<=x;i++){
		if(!vis[i]){
			pri[++cnt]=i;
			vis[i]=i;
			zs[i]=1;
		}
		for(int j=1;j<=cnt;j++){
			int tt=i*pri[j];
			if(tt>x||vis[i]<pri[j]) break;
			vis[tt]=pri[j];
			if(flag[i]==true) {
				flag[tt]=true;continue;
			}
			zs[tt]=zs[i];//开始i和tt的质因子个数是一样的
			tc[tt]=tc[i];//质因子指数为2的质因子个数也是一样的 
			if(i%pri[j]) zs[tt]++;
			else{
				if(i%(pri[j]*pri[j])==0) flag[tt]=true;//说明含有3个pri[j];
				else {
					tc[tt]++;
					if(tc[tt]>=2) flag[tt]=true; 
				}
			}
		}
	}
	for(int i=2;i<=x;i++)
	{
		if(flag[i]==true) f[i]=0;
		else{
			if(tc[i]==1) f[i]=js(i);
			else f[i]=js1(i);
		}	
	}	
	for(int i=1;i<=x;i++) sum[i]=sum[i-1]+f[i];
}
ll solve(int x,int y){
	ll ans=0;
	for(int l=1,r=0;l<=x;l=r+1){
		r=min(x/(x/l),y/(y/l));
		ans+=((1ll*x)/l)*((1ll*y)/l)*(1ll*(sum[r]-sum[l-1]));
	}
	return ans;
}
int main(){
	scanf("%d",&t);
	seive(10000000);	
	for(int i=1;i<=t;i++){
		scanf("%d%d",&n,&m);
		if(n>m) swap(n,m);
		ll ans=solve(n,m);
		printf("%lld\n",ans);
	}
}

算法四:

\[设T=p_1^{x_1}p_2^{x_2}p_3^{x_3}……p_k^{x_k},S=p_1^{{x_1}-1}p_2^{x_2}p_3^{x_3}……p_k^{x_k},T=S*p_1,我们尝试寻找S和T的递推关系\\ 情况一:如果x_1\geq 2,则s\%p_1=0,\\ g(T)=\sum_{i=1}^{k}{\mu{(\frac{T}{p}})}=\mu{(\frac{T}{p_1})}+\sum_{i=2}^{k}{\mu{(\frac{T}{p_i}})}=\mu{(S)}+\sum_{i=2}^{k}{\mu{(\frac{T}{p_i}})}\\ 上式中,第二部分的值为0,所以g(T)=\mu{(S)}\\ 情况二:如果x_1=1,则s\%p_1\neq0,\\ g(T)=\sum_{i=1}^{k}{\mu{(\frac{T}{p}})}=\mu{(\frac{T}{p_1})}+\sum_{i=2}^{k}{\mu{(\frac{T}{p_i}})}\\=\mu{(S)}+\sum_{i=2}^{k}{\mu{(\frac{S*p_1}{p_i}})}=\mu{(S)}+\sum_{i=2}^{k}{\mu(p_1)*\mu{(\frac{S}{p_i}})}=\mu{(S)}-\sum_{i=2}^{k}*{\mu{(\frac{S}{p_i}})}\\ =\mu{(S)}-g(S) \\可以在O(n)中预处理g函数的值 \]

#include<bits/stdc++.h>
using namespace std;
const int maxn=10000005;
typedef long long ll;
int t,pri[1000005],sum[maxn],n,m,vis[maxn],cnt,f[maxn],mu[maxn];

void seive(int x){
	mu[1]=1;
	for(int i=2;i<=x;i++){
		if(!vis[i]){
			pri[++cnt]=i;
			vis[i]=i;
			mu[i]=-1;
			f[i]=1;
		}
		for(int j=1;j<=cnt;j++){
			int tt=i*pri[j];
			if(tt>x||vis[i]<pri[j]) break;
			vis[tt]=pri[j];
			if(i%pri[j]==0) mu[tt]=0,f[tt]=mu[i];
			else mu[tt]=-mu[i],f[tt]=mu[i]-f[i];
		}
	}
	
	for(int i=1;i<=x;i++) sum[i]=sum[i-1]+f[i];
}
ll solve(int x,int y){
	ll ans=0;
	for(int l=1,r=0;l<=x;l=r+1){
		r=min(x/(x/l),y/(y/l));
		ans+=((1ll*x)/l)*((1ll*y)/l)*(1ll*(sum[r]-sum[l-1]));
	}
	return ans;
}
int main(){
	scanf("%d",&t);
	seive(10000000);	
	for(int i=1;i<=t;i++){
		scanf("%d%d",&n,&m);
		if(n>m) swap(n,m);
		ll ans=solve(n,m);
		printf("%lld\n",ans);
	}
}

例3:于神之怒

题目描述:给定$ n,m,k$,计算

\[\sum_{i=1}^n \sum_{j=1}^m \gcd(i,j)^k \]

\(10^9+7\) 取模的结果。

输入格式:本题单测试点内有多组测试数据

第一行有两个整数,分别表示数据组数T和给定的 k。

接下来 T行,每行两个整数,表示一组数据的n和 m。

输出格式:对于每组数据,输出一行一个整数表示答案。

样例输入 样例输出

1 2
3 3                                       20

数据规模与约定:

对于全部的测试点,保证$ 1 \leq T \leq 2 \times 10^3$,\(1 \leq n, m, k \leq 5 \times 10^6\)

算法一

\[ans=\sum_{i=1}^n \sum_{j=1}^m \gcd(i,j)^k\\ 按照公约数d对这个式子进行分类,ans=\sum_{d=1}^{n}{d^k}\sum_{k=1}^{\frac{n}{d}}{\frac{n}{kd}*\frac{m}{kd}*\mu{(k)}}\\ 枚举d,对于连续的d而言,后面的式子呈现整除分块的形态,详细来说,可以把d最多分成2(\sqrt{n}+\sqrt{m})个区域\\ 对于k的枚举,可以最多分成2*(\sqrt{\frac{n}{d}}+\sqrt{\frac{m}{d}})个区域,两部分都可以使用分块来处理\\ 单词询问时间复杂度为O(n),总的时间复杂度为O(TN)\\ \]

算法二

\[ans=\sum_{d=1}^{n}{d^k}\sum_{k=1}^{\frac{n}{d}}{\frac{n}{kd}*\frac{m}{kd}*\mu{(k)}}\\ 设T=kd,k=\frac{T}{d}\\ ans=\sum_{d=1}^{n}{d^k}\sum_{T=1}^{n}{\frac{n}{T}*\frac{m}{T}*\mu{(\frac{T}{d})}}\\ ans=\sum_{T=1}^{n}{\frac{n}{T}*\frac{m}{T}\sum_{d|T}{d^k}*\mu{(\frac{T}{d})}}\\ 设g(T)=\sum_{d|T}{d^k}*\mu{(\frac{T}{d})},如果能预处理出g(T)的前缀和,则此题可解\\ g(T)符合狄利克雷卷积的形式,d^k是积性函数,\mu函数是积性函数,所以g(T)是积性函数\\ 设T=\prod_{i=1}^{k}{p_i^{x_i}},则g(T)=\prod_{i=1}^{k}{g({p_i^{x_i})}}\\ p_i^{x_i}的约数有1,p_i,p_i^{2}……p_i^{{x_i}-1},p_i^{x_i}\\ 对应的,\frac{T}{d}=p_i^{x_i},p_i^{{x_i}-1}……p_i,1,只有p_i和1的\mu值有意义\\ 所以g(T)=\prod_{i=1}^{k}{g({p_i^{x_i})}}=\prod_{i=1}^{k}{-p_i^{k*({x_i}-1)}+p_i^{k*x_i}}\\ =\prod_{i=1}^{k}{p_i^{k*({x_i}-1)}(p_i^{k}-1)}\\ =\prod_{i=1}^{k}{({p_i^{k})}^{({x_i}-1)}}(p_i^{k}-1)\\ 对于p_i的处理,我们可以在线性筛的过程中解决 \]

#include<bits/stdc++.h>
using namespace std;
const int maxn=5000005;
const int mod=1000000007;
typedef long long ll;
int n,m,k,t,g[maxn],pri[maxn],vis[maxn],cnt,f[maxn],sum[maxn];
int mul(int x,int s){
	int ans=1;
	while(s){
		if(s&1) ans=(1ll*ans)*(1ll*x)%mod;
		x=(1ll*x)*(1ll*x)%mod;
		s=s>>1;
	}
	return ans;
}
void seive(int x){
	g[1]=1;
	for(int i=2;i<=x;i++){
		if(!vis[i]){
			pri[++cnt]=i;
			vis[i]=i;
			f[cnt]=mul(i,k);
			g[i]=(f[cnt]-1+mod)%mod;//f是一个模完了之后的数字,所以很可能等于0或者1 
			
		}
		for(int j=1;j<=cnt;j++){
			int tt=i*pri[j];
			if(tt>x||vis[i]<pri[j]) break;
			vis[tt]=pri[j];
            //如果出现一次重复的质因子,乘以f函数,一共乘以xi-1次
			if(i%pri[j]==0) g[tt]=(1ll*g[i])*(1ll*f[j])%mod;
            //新增一个质因子的时候,乘以g函数
			else g[tt]=(1ll*g[i])*(1ll*g[pri[j]])%mod;
		}
	}
	for(int i=1;i<=x;i++) sum[i]=(sum[i-1]+g[i])%mod;
}
ll solve(int x,int y){
	ll ans=0;
	for(int l=1,r=0;l<=x;l=r+1){
		r=min(x/(x/l),y/(y/l));
		ans+=(1ll*x/l)*(1ll*y/l)*(1ll*(sum[r]-sum[l-1]+mod))%mod;
	}
	return ans%mod;
}
int main(){
	scanf("%d%d",&t,&k);
	seive(5000000);
	for(int i=1;i<=t;i++){
		scanf("%d%d",&n,&m);
		if(n>m) swap(n,m);
		ll ans=solve(n,m);
		printf("%lld\n",ans);
	}
}

算法三

\[这里提供另一种思路\\ 设g(T)=\sum_{d|T}{d^k}*\mu{(\frac{T}{d})},如果能预处理出g(T)的前缀和,则此题可解\\ 假设T=\prod_{i=1}^{k}{p_i^{x_i}},d=\prod_{i=1}^{k}{p_i^{y_i}}\\ 如果使得\mu(\frac{T}{d})有贡献,则x_i=y_i或者x_i=y_i-1\\ 考虑y_i的值,设u=p_i^{x_i},v=p_i^{y_i},d=d_1*v,得到两种情况\\ 如果y_i=x_i,u=v,g(T)=\sum_{d|T}{d^k}{\mu{(\frac{T}{d})}}=v^k\sum_{d_1|\frac{T}{u}}{d_1^k}{\mu(\frac{\frac{T}{u}}{d_1})}=p_i^{x_i*k}*g(\frac{T}{u})\\ 如果y_i=x_i-1,则u=v*p_i,g(T)=\sum_{d|T}{d^k}{\mu{(\frac{T}{d})}}=v^k\sum_{d_1|\frac{T}{u}}{d_1^k}{\mu(\frac{\frac{T}{u}}{d_1}*p_i)}=-p_i^{(x_i-1)*k}*g(\frac{T}{u})\\ 综上两种情况,g(T)=p_i^{x_i*k}*g(\frac{T}{u})-p_i^{(x_i-1)*k}*g(\frac{T}{u})=\prod_{i=1}^{k}{g(p_i^{x_i})}=\prod_{i=1}^{k}{(p_i^k-1)p_i^{{(x_i-1)}*k}} \]

例4:Crash的数字表格 / JZPTAB

今天的数学课上,Crash 小朋友学习了最小公倍数(Least Common Multiple)。对于两个正整数 \(a 和 b\)\(lcm(a,b)\) 表示能同时整除 $a和 b \(的最小正整数。例如,\)lcm(6,8)$=24。

回到家后,Crash 还在想着课上学的东西,为了研究最小公倍数,他画了一张$ n×m\(的表格。每个格子里写了一个数字,其中第 i行第 j列的那个格子里写着数为\)lcm(i,j)$。

看着这个表格,Crash 想到了很多可以思考的问题。不过他最想解决的问题却是一个十分简单的问题:这个表格中所有数的和是多少。当 \(n 和 m\)很大时,Crash 就束手无策了,因此他找到了聪明的你用程序帮他解决这个问题。由于最终结果可能会很大,Crash 只想知道表格里所有数的和 mod 20101009 的值。

输入格式:输入包含一行两个整数,分别表示 n和 m。

输出格式:输出一个正整数,表示表格中所有数的和 mod 20101009的值。

**输入 ** 输出

4 5                      122

数据规模与约定

  • 对于 30% 的数据,保证 \(n, m \le 10^3\)
  • 对于70% 的数据,保证 \(n, m \le 10^5\)
  • 对于 100% 的数据,保证\(1\le n,m \le 10^7\)

算法一:

\[ans=\sum_{x=1}^{n}\sum_{y=1}^{m}{lcm(x,y)}=\sum_{x=1}^{n}\sum_{y=1}^{m}{\frac{x*y}{gcd(x,y)}},设gcd(x,y)=d\\ ans=\sum_{d=1}^{n}{\frac{1}{d}}\sum_{1\leq x \leq n,1\leq y \leq m,gcd(x,y)=d}{x*y}\\ 设f(d)=\sum_{1\leq x \leq n,1\leq y \leq m,gcd(x,y)=d}{x*y},那么ans=\sum_{d=1}^{n}{\frac{f(d)}{d}}\\ 设g(d)为x \in [1,n],y \in [1.m],d|gcd(x,y)的x*y的和,\\ 每一对都有公因子d,相乘起来就是d^2,他们的系数分别从1到{\frac{n}{d}}和{\frac{m}{d}},成等差数列求和,所以\\ g(d)=\sum_{k=1}^{\frac{n}{d}}f(k*d)=d^2*{\frac{{\frac{n}{d}}({\frac{n}{d}+1})}{2}}*{\frac{{\frac{m}{d}}({\frac{m}{d}+1})}{2}},根据莫比乌斯反演变形一可得:\\ f(d)=\sum_{k=1}^{\frac{n}{d}}{g(kd)\mu{(k)}}=\sum_{k=1}^{\frac{n}{d}}\mu{(k)}d^2k^2*{\frac{{\frac{n}{kd}}({\frac{n}{kd}+1})}{2}}*{\frac{{\frac{m}{kd}}({\frac{m}{kd}+1})}{2}}\\ ans=\sum_{d=1}^{n}{d}\sum_{k=1}^{\frac{n}{d}}\mu{(k)}k^2*{\frac{{\frac{n}{kd}}({\frac{n}{kd}+1})}{2}}*{\frac{{\frac{m}{kd}}({\frac{m}{kd}+1})}{2}}\\ 和于神之怒的算法一类似,d分块,后面的也分块,时间复杂度为O(n) \]

算法二

\[ans=\sum_{d=1}^{n}{d}\sum_{k=1}^{\frac{n}{d}}\mu{(k)}k^2*{\frac{{\frac{n}{kd}}({\frac{n}{kd}+1})}{2}}*{\frac{{\frac{m}{kd}}({\frac{m}{kd}+1})}{2}}\\ 设T=kd,则ans=\sum_{T=1}^{n}\sum_{k=1}^{\frac{n}{d}}\mu{(k)}*k*T*{\frac{{\frac{n}{T}}({\frac{n}{T}+1})}{2}}*{\frac{{\frac{m}{T}}({\frac{m}{T}+1})}{2}}\\ ans=\sum_{T=1}^{n}T*{\frac{{\frac{n}{T}}({\frac{n}{T}+1})}{2}}*{\frac{{\frac{m}{T}}({\frac{m}{T}+1})}{2}}\sum_{k|T}\mu{(k)}*k\\ 设g(T)=\sum_{k|T}\mu{(k)}*k,如果能求出g(T)函数的前缀和,则O(\sqrt{n})内可得多次询问\\ 方法一:倍数法枚举,\sum_{i=1}^{n}{\frac{n}{i}}=n\ln n,超时;\\ 方法二:根据莫比乌斯性质,g(T)为积性函数,g(T)=\prod_{i=1}^{k}g(p_i^{x_i})\\ 要使得\mu(k)的值有贡献,只有k=1和p_i时, 所以g(T)=\prod_{i=1}^{k}{1-p_i},g(T)函数可以在o(n)的线性筛中完成,再维护T*g(T)的前缀和即可 \]

例5:【SDOI2014】数表

有一张 n×m 的数表,其第 i行第 j列(\(1\le i\le n,1\le j\le m\))的数值为能同时整除 i和 j的所有自然数之和。给定 a,计算数表中不大于 a 的数之和。

输入格式:输入包含多组数据。输入的第一行一个整数 Q表示测试点内的数据组数

接下来 Q行,每行三个整数 n,m,a(\(|a|\le 10^9\))描述一组数据。

输出格式:对每组数据,输出一行一个整数,表示答案模 \(2^{31}\)的值。

**输入 ** 输出

2
4 4 3                              20
10 10 5                            148

数据范围:

\(1\le n,m\le 10^5,1\le Q\le 2\times 10^4\)

算法一

\[整除i的自然数表示是i的约数,能同时整除i和j的自然数,表示既是i的约数,同时也是j的约数的所有自然数之和\\ 所以是gcd(i,j)=d的约数和 如果我们不考虑a的限制\\ 我们设定f(i)表示i的约数和\\ ans=\sum_{x=1}^{n}\sum_{y=1}^{m}f(gcd(x,y)),假设gcd(x,y)=d,我们把结果按照d分类\\ ans=\sum_{d=1}^{n}f(d)\sum_{x=1}^{n}\sum_{y=1}^{m}[gcd(x,y)=d]\\ 根据莫比乌斯反演变形一,可得\\ ans=\sum_{d=1}^{n}f(d)\sum_{k=1}^{\frac{n}{d}}\frac{n}{kd}*\frac{n}{kd}*\mu{(k)}\\ 按照之前的套路,设定T=kd,但是函数里面一个是变量d,一个是变量k,k=\frac{T}{d}\\ 转换成一个变量d,则原来的式子变成\\ ans=\sum_{T=1}^{n}{\frac{n}{T}}*{\frac{m}{T}}\sum_{d|T}f(d)*{\mu{(\frac{T}{d})}}\\ 设定g(T)=\sum_{d|T}f(d)*{\mu{(\frac{T}{d})}},设法求他的前缀和\\ 第一种方法:g(T)函数是积性函数\\ g(T)=\prod_{i=1}^{k}g(p_i^{x_i})=\prod_{i=1}^{k}(\frac{(p_i^{x_i}-1)}{p_i-1}*\mu(p_i^{x_i})+\frac{(p_i^{x_i+1}-1)}{p_i-1}*\mu(1))=\prod_{i=1}^{k}{p_i}^{x_i}=T\\ 第二种方法:根据倍数法,计算g(T)函数,时间复杂度为O(\sum_{i=1}^{n}{\frac{n}{i}}=n\ln n)\\ 接下来我们考虑a的限制,我们可以离线处理,把答案按照a升序排序\\ 把f(d)进行排序,对于第i个询问,把<=a 的f(d)函数插入进去,所以我们不能使用方法一来做\\ 必须使用方法二来做,使用树状数组来做,将f(d)*\mu 函数的值动态插入,求前缀和\\ 时间复杂度为O(n(\ln n)^2+q\sqrt{n}\ln n)??? \]

/**
代码小技巧:如何对2^31次方取模
答案要求对2的31次方取模,为什么我们需要将int定义成unsigned int?
我们知道unsigned int的取值范围是0--2^32-1,这样的取值范围正好是对2^32取模
有一个前置知识,如果我们对x取模,可以先对kx取模,最后再对x取模也行
所以,这里的k是2,x是2^31,kx正好是2^32 
相当于先落在0—kx-1,0-kx-1会分成k个0-x-1的区间,再对x取模后,就落在0-x-1区间 
**/ 
#include<bits/stdc++.h>
using namespace std;
#define int unsigned int  //unsigned int的取值范围为2^32 
const int mod=pow(2,31);
const int maxn=100005;
int pri[maxn],mu[maxn],vis[maxn],n,m,q,cnt,f[maxn],mx=100000;
struct Ys{
	int id,sum;
}ys[maxn];
struct Qu{
	int id,n,m,a;
}qu[20005];
int ans1[20005];
bool cmp(const Qu x,const Qu y){
	return x.a<y.a;
}
bool cmp1(const Ys x,const Ys y){
	return x.sum<y.sum;
}
void seive(){
	mu[1]=1;
	for(int i=2;i<=mx;i++){
		if(!vis[i]){
			pri[++cnt]=i;
			mu[i]=-1;
			vis[i]=i;
		}
		for(int j=1;j<=cnt;j++){
			int tt=i*pri[j];
			if(tt>mx||vis[i]<pri[j]) break;
			vis[tt]=pri[j];
			if(i%pri[j]==0) mu[tt]=0;
			else mu[tt]=-mu[i];
		}
	}
	for(int i=1;i<=mx;i++){
		ys[i].id=i;
		for(int j=1;j<=mx/i;j++)
			ys[i*j].sum+=i;
	}
}
signed lowbit(int x){
	return x&(-x);
}
void insert(int x,int val){
	for(;x<=mx;x+=lowbit(x))f[x]+=val;
}
int ask(int x){
	if(x==0) return 0;
	int sum=0;
	for(;x>0;x=x-lowbit(x)) sum+=f[x];
	return sum;
}
void solve(int x,int y,int id){
	int ans=0;
	int bj=min(x,y);
	for(int l=1,r;l<=bj;l=r+1){
		r=min(x/(x/l),y/(y/l));
		ans+=(x/l)*(y/l)*(ask(r)-ask(l-1));
	}
	ans1[id]=ans;
}
signed main(){//为什么这里必须这么写? 
	seive();
	scanf("%ud",&q);
	for(int i=1;i<=q;i++){
		scanf("%u",&qu[i].n);
		scanf("%u",&qu[i].m);
		scanf("%u",&qu[i].a);
		qu[i].id=i;
	} 
	sort(qu+1,qu+1+q,cmp);
	sort(ys+1,ys+1+mx,cmp1);
	//for(int i=1;i<=20;i++) cout<<mu[i]<<endl;
	int st=1;
	for(int i=1;i<=q;i++){
		/**如果st的约数和小于当前的a,那么在树状数组上更新她的值,但是她的倍数也受影响
		所以他的倍数也应该在线段树上进行更新
		但是他的倍数不一定被更新完成了**/
		while(ys[st].sum<=qu[i].a&&st<=mx){
			int k=ys[st].id;
			for(int j=k;j<=mx;j+=k){
				insert(j,ys[st].sum*mu[j/k]);	
			}
			st++;
		}
		solve(qu[i].n,qu[i].m,qu[i].id);
	}
	for(int i=1;i<=q;i++)
		printf("%u\n",ans1[i]%mod);
	return 0;
}

例6:[SDOI2015]约数个数和

题目描述

设$ d(x)表示x \(的约数个数,给定 n,m,求\)\sum_{i=1}n\sum_{j=1}md(ij)$

输入格式:输入文件包含多组测试数据。第一行,一个整数 T,表示测试数据的组数。接下来的 T行,每行两个整数 n,m。

输出格式:T行,每行一个整数,表示你所求的答案。

输入 输出

2
7 4                                 110
5 6                                 121

数据范围
对于 100%的数据,\(1\le T,n,m \le 50000。\)

\[首先给出一个式子,d(nm)=\sum_{i|n}\sum_{j|m}[gcd(i,j)=1]\\ 证明:设nm=\prod_{i=1}^{k}p_i^{x_i},d(nm)=\prod_{i=1}^{k}(x_i+1)\\ 设n=\prod_{i=1}^{k}p_i^{y_i},则m=\prod_{i=1}^{k}p_i^{x_i-y_i}\\ 设i|n,j|m,设i=\prod_{i=1}^{k}p_i^{a_i},j=\prod_{i=1}^{k}p_i^{b_i}\\ 如果gcd(i,j)=1,则a_i=0或者b_i=0\\ 如果a_i=0,则b_i可以取值为0到x_i-y_i\\ 如果b_i=0,则a_i可以取值为0到y_i\\ 根据加法原理,共有x_i-y_i+1+y_i+1-1=x_i+1;这里为什么要减1呢?\\ 因为0重复了,如果两个都取0,表示没有p_i这个质因子\\ 对于p_i这个质因子是x_i+1,根据乘法原理\\ \sum_{i|n}\sum_{j|m}[gcd(i,j)=1]=\prod_{i=1}^{k}x_i+1=d(nm),原式子得证\\ \]

\[ans=\sum_{x=1}^{n}\sum_{y=1}^{m}d(xy)=\sum_{x=1}^{n}\sum_{y=1}^{m}\sum_{i|n}\sum_{j|m}[gcd(i,j)=1]\\ 交换枚举顺序,ans=\sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)=1]\sum_{x=1}^{n}\sum_{j=1}^{m}(i|n,j|m)\\ ans=\sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)=1]*\frac{n}{i}*\frac{m}{j}\\ 根据莫比乌斯反演,替换[gcd(i,j)=1]\\ ans=\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{k|gcd(i,j)}\mu(k)*\frac{n}{i}*\frac{m}{j}\\ 将\mu(k)提前,ans=\sum_{i=1}^{n}\mu(k)\sum_{i=1}^{n}\sum_{j=1}^{m}[k|i]*[k|j]*\frac{n}{i}*\frac{m}{j}\\ ans=\sum_{i=1}^{n}\mu(k)\sum_{i=1}^{n}[k|i]*\frac{n}{i}\sum_{j=1}^{m}*[k|j]*\frac{m}{j}\\ ans=\sum_{i=1}^{n}\mu(k)\sum_{i=1}^{\frac{n}{k}}\frac{n}{ik}\sum_{j=1}^{\frac{m}{k}}\frac{m}{jk}\\ 设f(i)=\sum_{i=1}^{\frac{n}{k}}\frac{n}{ik}\\ ans=\sum_{k=1}^{n}\mu(k)*f(i)*f(j)\\ 预处理f函数,在\sqrt(n)完成每次询问,总的时间复杂度为O(n+T\sqrt(n)) \]

#include<bits/stdc++.h>
using namespace std;
const int maxn=50005;
typedef long long ll;
int n,m,mu[maxn],sum[maxn],pri[maxn],vis[maxn],cnt,t;
ll g[maxn];
ll f(int x){
	ll ans=0;
	for(int l=1,r=0;l<=x;l=r+1){
		r=x/(x/l);
		ans+=1ll*(r-l+1)*(x/l);
	}
	return ans;
}
void seive(int x){
	mu[1]=1;
	for(int i=2;i<=x;i++){
		if(!vis[i]){
			vis[i]=i;
			mu[i]=-1;
			pri[++cnt]=i;
		}
		for(int j=1;j<=cnt;j++){
			int tt=i*pri[j];
			if(tt>x||vis[i]<pri[j]) break;
			vis[tt]=pri[j];
			if(i%pri[j]==0) mu[tt]=0;
			else mu[tt]=-mu[i]; 
		}
	}
	for(int i=1;i<=x;i++) sum[i]=sum[i-1]+mu[i];
	for(int l=1,r=0;l<=x;l++){
		g[l]=f(l);
	}
}

ll solve(int x,int y){
	ll ans=0;
	for(int l=1,r=0;l<=x;l=r+1){
		r=min(x/(x/l),y/(y/l));
		ans+=1ll*(sum[r]-sum[l-1])*g[x/l]*g[y/l];
	}
	return ans;
}
int main(){
	seive(50000);
	scanf("%d",&t);
	for(int i=1;i<=t;i++){
		scanf("%d%d",&n,&m);
		if(n>m) swap(n,m);
		ll ans=solve(n,m);
		printf("%lld\n",ans);
	}
}

总结

莫比乌斯反演应用的四大要点:

  1. 公式推导:利用莫比乌斯反演及其变形
  2. 等价变化:变量替换,内层变量外移,转换枚举顺序
  3. 合理利用线性筛预处理各种信息
  4. 整除分块处理
posted @ 2022-10-17 15:58  xinyimama  阅读(81)  评论(0)    收藏  举报