【笔记】数学

》》b站视频链接-郑州大学ACM实验室寒假新生培训Day5《《

》》b站视频链接-郑州大学ACM实验室寒假新生培训Day11《《

最近更新:2021.09.16

OP

在寒假直播的第五天我才发现这么一个宝藏教程,便推给了群里的大佬们,那时候正在忙牛客寒假赛,打算开学后再说,结果ph大佬竟然开学没多久就刷完了!!我反而没抽出空,直到最近才开始补~

留下此笔记,一是为了记录和理解知识点,把自己的知识点穿成线;二是充实一下博客,不至于比赛允许携带纸质材料时不知道印些什么,也不至于同学吐槽我的博客 太水了~

前四天直播的内容分别是:ACM及实验室简介,C++常用STL讲解,模拟、枚举、递推、递归,二分;
如果需要看可以在up主的投稿里面找;

此篇文章包括了Day5的内容,Day11的部分内容,和清华大学出版社《离散数学(第三版)》中的一些内容;

希望能够帮助到大家,如有发现错漏,欢迎评论更正,感激不尽;

快速幂

a b m o d    m a^b\mod m abmodm ( b ⩽ 1 e 18 ) (b\leqslant1e18) (b1e18);
PS: 这个蓝书一开头就讲了,印象很深
PS2:满足结合律的运算均可以用此类方法加速

对于任意的 b ,我们均可以将其转化为二进制表示,因此,对于 b ,我们有等式:

b = k 0 ⋅ 2 0 + k 1 ⋅ 2 1 + k 2 ⋅ 2 2 + . . . + k i ⋅ 2 i ; b=k_0\cdotp2^0+k_1\cdotp2^1+k_2\cdotp2^2+...+k_i\cdotp2^i; b=k020+k121+k222+...+ki2i;

将其代入 a b a^b ab,依据高中数学则有:

a b = a k 0 ⋅ 2 0 + k 1 ⋅ 2 1 + k 2 ⋅ 2 2 + . . . + k i ⋅ 2 i a^b=a^{k_0\cdotp2^0+k_1\cdotp2^1+k_2\cdotp2^2+...+k_i\cdotp2^i} ab=ak020+k121+k222+...+ki2i
= ( a 2 0 ) k 0 ⋅ ( a 2 1 ) k 1 ⋅ ( a 2 2 ) k 2 ⋅ . . . ⋅ ( a 2 i ) k i ; =(a^{2^0})^{k_0}\cdotp(a^{2^1})^{k_1}\cdotp(a^{2^2})^{k_2}\cdotp...\cdotp(a^{2^i})^{k_i}; =(a20)k0(a21)k1(a22)k2...(a2i)ki;

其中, k ∈ { 0 , 1 } k\in\{0,1\} k{01}
而且有 ( a 2 i ) = ( a 2 i − 1 ) 2 (a^{2^i})=(a^{2^{i-1}})^2 (a2i)=(a2i1)2​​​​;

那么我们在程序运行过程中,只需要维护好两个值,记录 b 取前 i 位时 ab 的值的 ans,以及记录 ( a 2 i ) (a^{2^i}) (a2i) 的值的变量 a ;

二进制数字 k i . . . k 2 k 1 k 0 ‾ \overline{k_i...k_2k_1k_0} ki...k2k1k0 正是b的二进制表示,所以程序运行中仅需要判断 b 的二进制第 i 位是否为1,以决定 ( a 2 i ) (a^{2^i}) (a2i) 是否乘入ans;

时间复杂度 O ( log ⁡ b ) O(\log b) O(logb)

代码
const ll M=1e9+7;
ll qm(ll a,ll b)//qm即quick_mod
{
	ll ans=1%M;//特殊处理M=1这一特殊情况
	for(;b;b>>=1)
	{
		if(b&1)ans=a*ans%M;//如果b此位为1
		a=a*a%M;
	}
	return ans;
}
矩阵快速幂

由于矩阵乘法同样满足结合律,所以矩阵乘幂也可以通过快速幂优化;

我们可以将矩阵以结构体中数组的方式存起来,方便传值;

struct mtx{
	int a[102][102];
};

由线性代数基础知识,得矩阵乘法 ( A B ) (AB) (AB) 代码如下;

mtx mul(mtx A,mtx B)
{
	mtx C;
	memset(C.a,0,sizeof(C.a));
	for(int i=0;i<100;i++)
		for(int j=0;j<100;j++)
			for(int k=0;k<100;k++)
			{
				C.a[i][j]+=A.a[i][k]*B.a[k][j];
			}
	return C;
}

快速幂计算矩阵幂次 ( A b ) (A^b) (Ab) 的代码如下:

mtx mqm(mtx A,int b)
{
	mtx ans;
	memset(ans.a,0,sizeof(ans.a));
	for(int i=0;i<100;i++)ans.a[i][i]=1;
	for(;b;b>>=1)
	{
		if(b&1)ans=mul(ans,A);
		A=mul(A,A);
	}
	return ans;
}

以上代码没有考虑取模,取模在mul函数中添加即可;

应用

例1

求斐波那契数列第 n 项对 M 取模之后的值( n ⩽ 1 e 18 n\leqslant1e18 n1e18

对于矩阵 [ a b ] \begin{bmatrix}a&b\end{bmatrix} [ab] ,右乘矩阵 [ 0 1 1 1 ] \begin{bmatrix}0&1\\1&1\end{bmatrix} [0111] ,可得矩阵 [ b a + b ] \begin{bmatrix}b&a+b\end{bmatrix} [ba+b]

类似地,我们可以依照斐波那契的递推公式获得以下结论

[ f ( n − 2 ) f ( n − 1 ) ] ⋅ [ 0 1 1 1 ] = [ f ( n − 1 ) f ( n ) ] \begin{bmatrix}f(n-2)&f(n-1)\end{bmatrix}\cdot\begin{bmatrix}0&1\\1&1\end{bmatrix}=\begin{bmatrix}f(n-1)&f(n)\end{bmatrix} [f(n2)f(n1)][0111]=[f(n1)f(n)]

以此类推:

[ f ( n − 2 ) f ( n − 1 ) ] [ 0 1 1 1 ] m = [ f ( n − 2 + m ) f ( n − 1 + m ) ] \begin{bmatrix}f(n-2)&f(n-1)\end{bmatrix}\begin{bmatrix}0&1\\1&1\end{bmatrix}^m=\begin{bmatrix}f(n-2+m)&f(n-1+m)\end{bmatrix} [f(n2)f(n1)][0111]m=[f(n2+m)f(n1+m)]

运用此规律,便可以通过矩阵快速幂取模快速计算斐波那契数列给定项的值;

注:斐波那契数列有封闭公式 f ( n ) = ( 1 + 5 2 ) n − ( 1 − 5 2 ) n 5 , ( n ⩾ 1 ) f(n)=\frac {(\frac {1+\sqrt 5}2)^n-(\frac {1-\sqrt 5}2)^n} {\sqrt 5},(n\geqslant1) f(n)=5 (21+5 )n(215 )n,(n1),可近似为 f ( n ) = ( 1 + 5 2 ) n 5 , ( n ⩾ 1 ) f(n)=\frac {(\frac {1+\sqrt 5}2)^n} {\sqrt 5},(n\geqslant1) f(n)=5 (21+5 )n,(n1)


例1的拓展

给定常系数线性齐次递推方程如下
{ H ( n ) = a 1 H ( n − 1 ) + a 2 H ( n − 2 ) + . . . + a k H ( n − k ) H ( 0 ) = b 0 , H ( 1 ) = b 1 , H ( 2 ) = b 2 , . . . , H ( k − 1 ) = b k − 1 \begin{cases} H(n)=a_1H(n-1)+a_2H(n-2)+...+a_kH(n-k)&\\ H(0)=b_0,H(1)=b_1,H(2)=b_2,...,H(k-1)=b_{k-1} \end{cases} {H(n)=a1H(n1)+a2H(n2)+...+akH(nk)H(0)=b0,H(1)=b1,H(2)=b2,...,H(k1)=bk1
这个递推方程除了用递推方程求解的方法处理,还可以用矩阵乘法表示成:
[ H ( n − k ) ⋯ H ( n − 2 ) H ( n − 1 ) ] [ 0 0 ⋯ 0 0 a k 1 0 ⋯ 0 0 a k − 1 0 1 ⋯ 0 0 a k − 2 ⋮ ⋮ ⋱ ⋮ ⋮ ⋮ 0 0 ⋯ 1 0 a 2 0 0 ⋯ 0 1 a 1 ] m \begin{bmatrix} H(n-k)&\cdots&H(n-2)&H(n-1) \end{bmatrix} \begin{bmatrix} 0&0&\cdots&0&0&a_{k}\\ 1&0&\cdots&0&0&a_{k-1}\\ 0&1&\cdots&0&0&a_{k-2}\\ \vdots&\vdots&\ddots&\vdots&\vdots&\vdots\\ 0&0&\cdots&1&0&a_2\\ 0&0&\cdots&0&1&a_1 \end{bmatrix}^m [H(nk)H(n2)H(n1)]01000001000001000001akak1ak2a2a1m

= [ H ( n − k + m ) ⋯ H ( n − 2 + m ) H ( n − 1 + m ) ] =\begin{bmatrix} H(n-k+m)&\cdots&H(n-2+m)&H(n-1+m) \end{bmatrix} =[H(nk+m)H(n2+m)H(n1+m)]

还找到了一个转置之后的版本,看起来比上面的方便阅读:
在这里插入图片描述图源


例2

给定a,b,求 ∑ i = a b F ( i ) \sum_{i=a}^bF(i) i=abF(i) F ( i ) F(i) F(i) 为斐波那契数列第 i 项)对M取模的值;

S ( n ) = ∑ i = 0 n F ( i ) = S ( n − 1 ) + F ( n − 1 ) + F ( n − 2 ) S(n)=\sum_{i=0}^nF(i)=S(n-1)+F(n-1)+F(n-2) S(n)=i=0nF(i)=S(n1)+F(n1)+F(n2)
这样我们就可以构造矩阵了;

[ 1 1 0 0 1 1 0 1 0 ] [ S ( i ) F ( i ) F ( i − 1 ) ] = [ S ( i + 1 ) F ( i + 1 ) F ( i ) ] \begin{bmatrix} 1&1&0\\ 0&1&1\\ 0&1&0 \end{bmatrix} \begin{bmatrix} S(i)\\ F(i)\\ F(i-1) \end{bmatrix}= \begin{bmatrix} S(i+1)\\ F(i+1)\\ F(i) \end{bmatrix} 100111010S(i)F(i)F(i1)=S(i+1)F(i+1)F(i)

进一步地:

[ 1 1 0 0 1 1 0 1 0 ] n [ S ( 1 ) F ( 1 ) F ( 0 ) ] = [ S ( 1 + n ) F ( 1 + n ) F ( n ) ] \begin{bmatrix} 1&1&0\\ 0&1&1\\ 0&1&0 \end{bmatrix}^n \begin{bmatrix} S(1)\\ F(1)\\ F(0) \end{bmatrix}= \begin{bmatrix} S(1+n)\\ F(1+n)\\ F(n) \end{bmatrix} 100111010nS(1)F(1)F(0)=S(1+n)F(1+n)F(n)

接下来求出 S ( b ) − S ( a − 1 ) S(b)-S(a-1) S(b)S(a1)​​​​ 即可;

当然,我们也可以推得 S ( n ) = f ( n + 2 ) − f ( 1 ) S(n)=f(n+2)-f(1) S(n)=f(n+2)f(1)

S ( n ) = f ( 0 ) + f ( 1 ) + ∑ i = 2 n f ( i ) = f ( 0 ) + f ( 1 ) + ∑ i = 2 n f ( i − 1 ) + ∑ i = 2 n f ( i − 2 )   = f ( 0 ) + f ( 1 ) + ( S ( i − 1 ) − f ( 0 ) ) + S ( i − 2 )   = f ( 1 ) + S ( n − 1 ) + S ( n − 2 )   ∵ S ( n ) = S ( n − 1 ) + f ( n )   ∴ f ( n ) = f ( 1 ) + S ( n − 2 )   ∴ S ( n ) = f ( n + 2 ) − f ( 1 ) S(n)=f(0)+f(1)+\sum_{i=2}^nf(i)\\ =f(0)+f(1)+\sum_{i=2}^nf(i-1)+\sum_{i=2}^nf(i-2)\\ \ \\ =f(0)+f(1)+(S(i-1)-f(0))+S(i-2)\\ \ \\ =f(1)+S(n-1)+S(n-2)\\ \ \\ \because S(n)=S(n-1)+f(n)\\ \ \\ \therefore f(n)=f(1)+S(n-2)\\ \ \\ \therefore S(n)=f(n+2)-f(1) S(n)=f(0)+f(1)+i=2nf(i)=f(0)+f(1)+i=2nf(i1)+i=2nf(i2) =f(0)+f(1)+(S(i1)f(0))+S(i2) =f(1)+S(n1)+S(n2) S(n)=S(n1)+f(n) f(n)=f(1)+S(n2) S(n)=f(n+2)f(1)

故亦可求出 f ( b + 2 ) − f ( a + 1 ) f(b+2)-f(a+1) f(b+2)f(a+1)​ ;


例3

定义 f ( n ) = x f ( n − 1 ) + y f ( n − 2 ) , S ( n ) = ∑ i = 0 n f ( i ) 2 f(n)=xf(n-1)+yf(n-2),S(n)=\sum_{i=0}^nf(i)^2 f(n)=xf(n1)+yf(n2),S(n)=i=0nf(i)2 , 求 S ( n ) S(n) S(n)

同样地,由于

S ( n ) = S ( n − 1 ) + f ( n ) 2 = S ( n − 1 ) + x 2 f ( n − 1 ) 2 + 2 x y f ( n − 1 ) f ( n − 2 ) + y 2 f ( n − 2 ) 2 S(n)=S(n-1)+f(n)^2=S(n-1)+x^2f(n-1)^2+2xyf(n-1)f(n-2)+y^2f(n-2)^2 S(n)=S(n1)+f(n)2=S(n1)+x2f(n1)2+2xyf(n1)f(n2)+y2f(n2)2

得到矩阵乘法形式

[ 1 x 2 y 2 2 x y 0 x 2 y 2 2 x y 0 1 0 0 0 x 0 y ] [ S ( i − 1 ) f ( i − 1 ) 2 f ( i − 2 ) 2 f ( i − 1 ) f ( i − 2 ) ] \begin{bmatrix} 1&x^2&y^2&2xy\\ 0&x^2&y^2&2xy\\ 0&1&0&0\\ 0&x&0&y \end{bmatrix} \begin{bmatrix} S(i-1)\\ f(i-1)^2\\ f(i-2)^2\\ f(i-1)f(i-2) \end{bmatrix} 1000x2x21xy2y2002xy2xy0yS(i1)f(i1)2f(i2)2f(i1)f(i2)

= [ S ( i − 1 ) + x 2 f ( i − 1 ) 2 + y 2 f ( i − 2 ) 2 + 2 x y f ( i − 1 ) f ( i − 2 ) x 2 f ( i − 1 ) 2 + y 2 f ( i − 2 ) 2 + 2 x y f ( i − 1 ) f ( i − 2 ) f ( i − 1 ) 2 f ( i − 1 ) ( x f ( i − 1 ) + y f ( i − 2 ) ) ] = [ S ( i ) f ( i ) 2 f ( i − 1 ) 2 f ( i ) f ( i − 1 ) ] =\begin{bmatrix} S(i-1)+x^2f(i-1)^2+y^2f(i-2)^2+2xyf(i-1)f(i-2)\\ x^2f(i-1)^2+y^2f(i-2)^2+2xyf(i-1)f(i-2)\\ f(i-1)^2\\ f(i-1)(xf(i-1)+yf(i-2)) \end{bmatrix}= \begin{bmatrix} S(i)\\ f(i)^2\\ f(i-1)^2\\ f(i)f(i-1) \end{bmatrix} =S(i1)+x2f(i1)2+y2f(i2)2+2xyf(i1)f(i2)x2f(i1)2+y2f(i2)2+2xyf(i1)f(i2)f(i1)2f(i1)(xf(i1)+yf(i2))=S(i)f(i)2f(i1)2f(i)f(i1)

同理

[ 1 x 2 y 2 2 x y 0 x 2 y 2 2 x y 0 1 0 0 0 x 0 y ] n [ S ( 1 ) f ( 1 ) 2 f ( 0 ) 2 f ( 1 ) f ( 0 ) ] = [ S ( 1 + n ) f ( 1 + n ) 2 f ( n ) 2 f ( 1 + n ) f ( n ) ] \begin{bmatrix} 1&x^2&y^2&2xy\\ 0&x^2&y^2&2xy\\ 0&1&0&0\\ 0&x&0&y \end{bmatrix}^n \begin{bmatrix} S(1)\\ f(1)^2\\ f(0)^2\\ f(1)f(0) \end{bmatrix}= \begin{bmatrix} S(1+n)\\ f(1+n)^2\\ f(n)^2\\ f(1+n)f(n) \end{bmatrix} 1000x2x21xy2y2002xy2xy0ynS(1)f(1)2f(0)2f(1)f(0)=S(1+n)f(1+n)2f(n)2f(1+n)f(n)

欧几里得算法

即辗转求余,C++中可以直接使用__gcd(a,b)来求 a 与 b 的最大公约数;

若有 d = g c d ( x , y ) d=gcd(x,y) d=gcd(x,y)
则有 d ∣ x , d ∣ y ; d|x,d|y; dx,dy;
由于 x = a d , y = b d x=ad,y=bd x=ad,y=bd
d ∣ ( x − y ) ; d|(x-y); d(xy);
则有 g c d ( x , y ) = g c d ( x − y , y ) ; gcd(x,y)=gcd(x-y,y); gcd(x,y)=gcd(xy,y);
以此类推, g c d ( x , y ) = g c d ( x − 2 y , y ) = g c d ( x − 3 y , y ) = . . . ; gcd(x,y)=gcd(x-2y,y)=gcd(x-3y,y)=...; gcd(x,y)=gcd(x2y,y)=gcd(x3y,y)=...;
化作取模 g c d ( x , y ) = g c d ( x % y , y ) ; gcd(x,y)=gcd(x\%y,y); gcd(x,y)=gcd(x%y,y);

时间复杂度 O ( log ⁡ m a x ( x , y ) ) O(\log max(x,y)) O(logmax(x,y))

关于时间复杂度

规律:每次计算后,最大数至少被折半;

证明:
假设较大数为x,较小数为y;
y ⩽ x / 2 y\leqslant x/2 yx/2 ,则有 x % y ⩽ y ⩽ x / 2 ; x\%y\leqslant y\leqslant x/2; x%yyx/2;
y > x / 2 y\gt x/2 y>x/2 ,则有 x % y = x − y < x / 2 ; x\%y=x-y<x/2; x%y=xy<x/2;
毕;

代码

贴一个c语言的吧,(实际上语法都一样)

ll gcd(ll x,ll y)
{
	if(!y)return x;
	return gcd(x%y,x);
}//在具体处理中,我将x放入递归函数的y中,避免持续对一个数求余

最大公约数与最小公倍数

有以下两条定理:

  1. a ∣ m , b ∣ m a|m,b|m am,bm ,则 l c m ( a , b ) ∣ m lcm(a,b)|m lcm(a,b)m
  2. d ∣ a , d ∣ b d|a,d|b da,db ,则 d ∣ g c d ( a , b ) d|gcd(a,b) dgcd(a,b)

以上两条定理可由素因子分解(下文的唯一分解定理)简单证明;

整除分块

个人之前的题解(D)曾经用函数图形简略证明过这个问题;

规律:对于 ⌊ n k ⌋ ( k ∈ { 1 , 2 , . . . , n } ) \lfloor \frac n k\rfloor(k\in\{1,2,...,n\}) kn(k{1,2,...,n}) ,不同的值有 2 ⌊ n ⌋ 2\lfloor\sqrt n\rfloor 2n 2 ⌊ n ⌋ − 1 2\lfloor\sqrt n\rfloor-1 2n 1 个;

证明:

k ⩽ n k\leqslant\sqrt n kn 时,显然此时函数 f ( k ) = n / k f(k)=n/k f(k)=n/k 的导函数小于-1,即 k 每缩小1,n/x 的值增加多于1,故此时 ⌊ n k ⌋   / = ⌊ n k − 1 ⌋ \lfloor \frac n k\rfloor \mathrlap{\,/}{=}\lfloor \frac n {k-1}\rfloor kn/=k1n,此处有 ⌊ n ⌋ \lfloor\sqrt n\rfloor n 种不同取值;
此时 ⌊ n k ⌋ m i n ⩾ ⌊ n ⌋ \lfloor \frac n k\rfloor_{min}\geqslant\lfloor\sqrt n\rfloor knminn (仅 ⌊ n ⌋ 2 = n \lfloor\sqrt n\rfloor^2=n n 2=n 时取等)

k > n k\gt \sqrt n k>n 时,除可利用函数图像直观验证外,还可做以下证明:
此种情况 k m i n = ⌊ n ⌋ + 1 ; k_{min}=\lfloor \sqrt n\rfloor+1; kmin=n +1;
由初中数学易验证 ⌊ n ⌋ − 1 < n ⌊ n ⌋ + 1 < ⌊ n ⌋ + 1 \lfloor\sqrt n\rfloor-1<\frac n {\lfloor \sqrt n\rfloor+1}<\lfloor\sqrt n\rfloor+1 n 1<n +1n<n +1
那么

⌊ n ⌊ n ⌋ + 1 ⌋ = { ⌊ n ⌋   ( n ⩾ ⌊ n ⌋ 2 + ⌊ n ⌋ ) (such as  8 , 12 , 89 ) ⌊ n ⌋ − 1   ( n < ⌊ n ⌋ 2 + ⌊ n ⌋ ) (such as  9 , 10 , 91 ) \lfloor\frac n {\lfloor \sqrt n\rfloor+1}\rfloor= \begin{cases} \lfloor \sqrt n\rfloor\ &(n \geqslant \lfloor \sqrt n\rfloor^2+\lfloor \sqrt n\rfloor) \text{(such as }8,12,89)\\ \lfloor \sqrt n\rfloor-1\ & (n \lt \lfloor \sqrt n\rfloor^2+\lfloor \sqrt n\rfloor) \text{(such as }9,10,91) \end {cases} n +1n={n  n 1 (nn 2+n )(such as 8,12,89)(n<n 2+n )(such as 9,10,91)

所以 ⌊ n ⌊ n ⌋ + 1 ⌋ , ⌊ n ⌊ n ⌋ + 2 ⌋ , . . . , ⌊ n n ⌋ \lfloor\frac n {\lfloor \sqrt n\rfloor+1}\rfloor,\lfloor\frac n {\lfloor \sqrt n\rfloor+2}\rfloor,...,\lfloor\frac n n\rfloor n +1n,n +2n,...,nn 的值均分布在 { 1 , 2 , . . . , ⌊ n ⌊ n ⌋ + 1 ⌋ } \{1,2,...,\lfloor\frac n {\lfloor \sqrt n\rfloor+1}\rfloor\} {1,2,...,n +1n}中,此处有 ⌊ n ⌋ \lfloor\sqrt n\rfloor n ⌊ n ⌋ − 1 \lfloor\sqrt n\rfloor-1 n 1​​ 种不同取值;

由于 ⌊ n ⌋ 2 = n \lfloor\sqrt n\rfloor^2=n n 2=n 时, n < ⌊ n ⌋ 2 + ⌊ n ⌋ n \lt \lfloor \sqrt n\rfloor^2+\lfloor \sqrt n\rfloor n<n 2+n 恒成立,故不存在两种情况下取相同值的情况;

代码

题目:
∑ k = 1 n ⌊ n k ⌋ , n ⩽ 1 e 9 \sum^n_{k=1}\lfloor \frac n k \rfloor,n\leqslant1e9 k=1nkn,n1e9

for(int l=1,r=1;l<=n;l=r+1)//每一次循环对应一个分块的 l,r,n/l
{
	r=n/(n/l);
	ans+=n/l*(r-l+1);
}

n l \frac n l ln 即为该分块的值;
同时,有 r n l ⩽ n r\frac n l\leqslant n rlnn
为使 r 取到最大值,则可以对于一个 l ,直接取 r = n n / l r=\frac n {n/l} r=n/ln

欧拉筛

插一句,暴力判断一个数是否为质数的时间复杂度为 O ( n ) O(\sqrt n) O(n )
求 1 ~ n 内的所有质数;

欧拉筛的特点:每个合数仅被其最小的质因子筛除,且仅被筛一次,时间复杂度 O ( n ) O(n) O(n)

代码
const ll N=1e7;
int p[N+7],c=0;//p[]存储第 i 个质数(from 0),c 作为末尾标记
bool n[N+7];//n[]存储整数 i 是否为质数,若标记 0 则为质数,标记 1 则为合数
void pri()
{
	n[0]=n[1]=1;//0,1 均不是质数
	for(int i=2;i<=N;i++)
	{
		if(!n[i])p[c++]=i;//若为质数,则加入 p 数组
		for(int j=0;j<c&&i*p[j]<=N;j++)
		{
			n[i*p[j]]=1;
			if(i%p[j]==0)break;
		}
	}
}
时间复杂度证明

维持线性时间复杂度的关键是语句if(i%p[j]==0)break;

对于每一个数被筛掉的情况,我手动模拟出了这样一张图
在这里插入图片描述
其中描述了合数是如何被筛掉的;
对于数字12,我们观察到其被2*6筛掉,而不是3*4,因为4在筛掉2*4=8之后就break掉了;

所以,我们能观察到如下规律:
若合数 h h h 的最小质因数为 p i p_i pi ,则对于合数 p i + 1 ∗ h p_{i+1}*h pi+1h 一定有 p i ∗ ( h ∗ p i + 1 / p i ) p_i*(h*p_{i+1}/p_i) pi(hpi+1/pi) ,其中,一定存在 p i < p i + 1 p_i\lt p_{i+1} pi<pi+1 ,所以以 p i + 1 ∗ h p_{i+1}*h pi+1h 筛掉该合数不满足被最小质因数筛掉这一要求;
换言之,如果不在 i%p[j]==0 时break掉,则一定有数被筛掉多次,造成时间上的浪费;
综上,每个数仅被筛掉一次,时间复杂度 O ( n ) O(n) O(n)

同时也可以获取到每个数的最小质因子;

再进行一点点优化

平常比赛时候一般用不到,只是了解过
来自此链接
大意如下:

除2外每一个质数一定满足 6 n ± 1 6n\pm1 6n±1
理由如下:
( 6 n ) % 6 = 0 (6n)\%6=0 (6n)%6=0
( 6 n + 2 ) % 2 = 0 (6n+2)\%2=0 (6n+2)%2=0
( 6 n + 3 ) % 3 = 0 (6n+3)\%3=0 (6n+3)%3=0
( 6 n + 4 ) % 2 = 0 (6n+4)\%2=0 (6n+4)%2=0

所以筛的时候只需要筛 n / 3 就好啦

素数的分布

有无穷多个素数;

π ( n ) \pi(n) π(n) 为小于等于 n 的素数的个数,当 n ⩾ 67 n\geqslant67 n67 时:
ln ⁡ ( n ) − 3 2 ⩽ n π ( n ) ⩽ ln ⁡ ( n ) − 1 2 \ln(n)-\frac 3 2\leqslant\frac n {\pi(n)}\leqslant\ln(n)-\frac 1 2 ln(n)23π(n)nln(n)21
得推论(素数定理):
lim ⁡ n → + ∞ π ( n ) n / ln ⁡ n = 1 \lim_{n\to+\infty}\frac {\pi(n)}{n/\ln n}=1 n+limn/lnnπ(n)=1

整数的唯一分解定理

对于正整数 n ,仅存在一种分解方式将其分解为若干质数的乘积:

n = p 1 t 1 p 2 t 2 . . . p k t k n=p_1^{t_1}p_2^{t_2}...p_k^{t_k} n=p1t1p2t2...pktk

通常与欧拉筛搭配使用以降低时间复杂度;

代码

方法一:在筛出素数后再进行试除;

const ll N=1e7;
int p[N+7],c=0;
bool n[N+7];
void pri()
{
	n[0]=n[1]=1;
	for(int i=2;i<=N;i++)
	{
		if(!n[i])p[c++]=i;
		for(int j=0;j<c&&i*p[j]<=N;j++)
		{
			n[i*p[j]]=1;
			if(i%p[j]==0)break;
		}
	}
}
//以上为欧拉筛
int main()
{
    pri();
    int n;
    while(cin>>n)
    {
        for(int i=0;i<c&&p[i]<=n;i++)
        {
            int t=0;
            while(n%p[i]==0)
            {
                n/=p[i];
                t++;
            }
            //此时此处t即为pi的幂次
        }
    }
    return 0;
}

方法二:依照欧拉筛中每个数只会被自己的最小质因子标记的性质,记录每个数的最小质因子,再递归处理

const ll N=1e7;
int p[N+7],minp[N+7],c=0;
bool n[N+7];
void pri()
{
	n[0]=n[1]=1;
	for(int i=2;i<=N;i++)
	{
		if(!n[i])p[c++]=i,minp[i]=1;
		for(int j=0;j<c&&i*p[j]<=N;j++)
		{
			n[i*p[j]]=1;
			minp[i*p[j]]=p[j];
			if(i%p[j]==0)break;
		}
	}
}
//以上为欧拉筛
int main()
{
    pri();
    int n;
    while(cin>>n)
    {
        while(n!=1)
        {
       		//此时minp[n]即为n的最小质因子
        	n=minp[n];
        }
    }
    return 0;
}
例题

例1:n 有多少个因子?

对于 n 的任何一个因子 m ,都可以表示为

m = p 1 t m 1 p 2 t m 2 . . . p k t m k m=p_1^{t_{m1}}p_2^{t_{m2}}...p_k^{t_{mk}} m=p1tm1p2tm2...pktmk

其中, t m i ∈ [ 0 , t i ] t_{mi}\in[0,t_i] tmi[0,ti]
所以,一共有 ( t 1 + 1 ) ( t 2 + 1 ) . . . ( t k + 1 ) (t_1+1)(t_2+1)...(t_k+1) (t1+1)(t2+1)...(tk+1) 个;

const ll N=1e7;
int p[N+7],c=0;
bool n[N+7];
void pri()
{
	n[0]=n[1]=1;
	for(int i=2;i<=N;i++)
	{
		if(!n[i])p[c++]=i;
		for(int j=0;j<c&&i*p[j]<=N;j++)
		{
			n[i*p[j]]=1;
			if(i%p[j]==0)break;
		}
	}
}
//以上为欧拉筛
int main()
{
    pri();
    int n;
    while(cin>>n)
    {
        ll ans=1;
        for(int i=0;i<c&&p[i]<=n;i++)
        {
            int t=0;
            while(n%p[i]==0)
            {
                n/=p[i];
                t++;
            }
            ans*=t+1;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

只有这题是唯一分解定理板子,例二例三都不是


例2:n ! 有多少个因子?

由上题得,我们只需要将 n ! 表示为

n ! = p 1 t 1 p 2 t 2 . . . p k t k n!=p_1^{t_{1}}p_2^{t_{2}}...p_k^{t_{k}} n!=p1t1p2t2...pktk

即可求出因子数;
接下来我们只需要求出每一个质数的 ti

求 n ! 末尾有多少个 0 一题中,求取总共贡献 5 的个数的方法,即可类似地求得每一个质数的 ti

t i = ∑ j = 1 p i j ⩽ n ⌊ n p i j ⌋ t_i=\sum_{j=1}^{p_i^j\leqslant n}\lfloor \frac n {p_i^j}\rfloor ti=j=1pijnpijn

具体原理不做赘述;
时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)​​;


例3: ∏ i = 1 n i ! \prod_{i=1}^{n}i! i=1ni! 末尾有几个0?

首先明确,此题实际上是求在连乘值中,能拆出多少个 5 ;

对于 1 ! ⋅ 2 ! ⋅ . . . ⋅ i ! 1!\cdot 2!\cdot ...\cdot i! 1!2!...i! ,乘上的 5 有 i − 4 i-4 i4 个;
乘上的 10 有 i − 9 i-9 i9 个,其中每个10 有一个 5 ;
乘上的 15 有 i − 14 i-14 i14 个;

乘上的 25 有 i − 24 i-24 i24 个,其中每个 25 只取第一个 5 ;

所以,由等差数列求和, 1 ! ⋅ 2 ! ⋅ . . . ⋅ i ! 1!\cdot 2!\cdot ...\cdot i! 1!2!...i! 能贡献出 ( 2 i − 5 ⌊ i 5 ⌋ − 5 + 2 ) ⌊ i 5 ⌋ 2 \frac {(2i-5\lfloor\frac i 5 \rfloor-5+2)\lfloor \frac i 5\rfloor} 2 2(2i55i5+2)5i个“第一个” 5 ;
继续来说,
乘上的 25 有 i − 24 i-24 i24 个,其中每个 25 只取第二个 5 ;
乘上的 50 有 i − 49 i-49 i49 个,其中每个 50 只取第二个 5 ;

乘上的 125 有 i − 124 i-124 i124 个,其中每个 125 只取第二个 5 ;

同样,由等差数列求和, 1 ! ⋅ 2 ! ⋅ . . . ⋅ i ! 1!\cdot 2!\cdot ...\cdot i! 1!2!...i! 能贡献出 ( 2 − 25 ⌊ i 25 ⌋ − 25 + 2 ) ⌊ i 25 ⌋ 2 \frac {(2-25\lfloor\frac i {25} \rfloor-25+2)\lfloor \frac i {25}\rfloor} 2 2(22525i25+2)25i个“第二个” 5 ;

以此类推,一共可以贡献出

s = ∑ j = 1 5 j ⩽ n ( 2 n − 5 j ⌊ n 5 j ⌋ − 5 j + 2 ) ⌊ n 5 j ⌋ 2 s=\sum_{j=1}^{5^j\leqslant n}\frac {(2n-5^j\lfloor\frac n {5^j}\rfloor-5^j+2)\lfloor\frac n {5^j}\rfloor} {2} s=j=15jn2(2n5j5jn5j+2)5jn

s个5(此时的 j 即为“第 j 个 5 ”);
化简得(其实没必要)

s = ∑ j = 1 5 j ⩽ n ( n + 2 − 5 j + n % 5 j ) ⌊ n 5 j ⌋ 2 s=\sum_{j=1}^{5^j\leqslant n}\frac {(n+2-5^j+n\%5^j)\lfloor\frac n {5^j}\rfloor} {2} s=j=15jn2(n+25j+n%5j)5jn

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll M=10007;

int main()
{
    int n;
    while(cin>>n)
    {
        ll five=5;
        ll ans=0;
        while(five<=n)
        ans+=(n+2-five+n%five)*(n/five)/2,five=5*five;
        printf("%lld\n",ans);
    }
}

欧拉函数

欧拉函数 φ ( n ) \varphi(n) φ(n) 表示 [ 1 , n − 1 ] [1,n-1] [1,n1] 区间内,与 n 互质的数的个数;
定义有 φ ( 1 ) = 1 \varphi (1)=1 φ(1)=1
例如 φ ( 8 ) = 4  ,  ( 1 , 3 , 5 , 7 ) \varphi (8)=4\text{ , }(1,3,5,7) φ(8)=4 , (1,3,5,7)
若 n 为质数,则有 φ ( n ) = n − 1 \varphi(n)=n-1 φ(n)=n1

一般来说,有

φ ( x ) = x ∏ i = 1 n ( 1 − 1 p i ) \varphi(x)=x\prod^n_{i=1}(1-\frac 1 {p_i}) φ(x)=xi=1n(1pi1)

其中p1, p2……pn为x的所有质因数,x是不为0的整数(可由容斥定理证明)

还有一条性质如下:(积性函数)
m , n m,n m,n 互质,则有

φ ( m ⋅ n ) = φ ( m ) ⋅ φ ( n ) \varphi(m\cdotp n)=\varphi(m)\cdotp\varphi(n) φ(mn)=φ(m)φ(n)

代码
log求取某数欧拉函数值

由连乘式和整数分解,代码如下:

//在欧拉筛后
ll gphi(ll g)
{
    ll ans = g;
    if (g == 1)
        return 0;
    int sq = sqrt(g);
    for (int i = 0; i < c && p[i] <= sq; i++)
    {
        if (g % p[i] == 0)
        {
            ans = ans - ans / p[i];
            while (g % p[i] == 0)
            {
                g /= p[i];
            }
        }
    }
    if (g > 1)
        ans = ans - ans / g;
    return ans;
}
线性求取范围内的欧拉函数值

由上面的公式,我们可以得到两个递推式:
(其中p1, p2……pn为x的所有质因数,x是不为0的整数)

φ ( x p i ) = φ ( x ) p i   ( i ⩽ n ) \varphi(xp_i)=\varphi(x)p_i\text{ }(i\leqslant n) φ(xpi)=φ(x)pi (in)
φ ( x p j ) = φ ( x ) p j ( 1 − 1 p j ) = φ ( x ) ⋅ φ ( p j )   ( j > n ) \varphi(xp_j)=\varphi(x)p_j(1-\frac 1 {p_j})=\varphi(x)\cdotp \varphi(p_j)\text{ }(j\gt n) φ(xpj)=φ(x)pj(1pj1)=φ(x)φ(pj) (j>n)

由以上推论,代码实现可以与欧拉筛共同完成:

phi[1]= 1;
for(int i = 2 ; i < N ; i ++ )
{
    if(!n[i])
    {
        prime[c++] = i;
        phi[i] = i - 1;
    }
    for(int j = 0 ; j < c && (ll)i * prime[j] < N ; j ++ )
    {
        n[i * prime[j] ] = 1;
        if(i % prime[j] == 0)
        {
            phi[i * prime[j]] = phi[i] * prime[j];
            break;
        }
        phi[i * prime[j]] = phi[i] * phi[prime[j]];
    }
 }
例题

∑ i = 1 n ∑ j = 1 n [ g c d ( i , j ) = 1 ] , n ⩽ 1 e 7 \sum_{i=1}^n\sum_{j=1}^n[gcd(i,j)=1],n\leqslant1e7 i=1nj=1n[gcd(i,j)=1],n1e7 ;

对于任意一个 i ,比 i 小的数中与其互质的数有 φ ( i ) \varphi(i) φ(i) 个,即有 2 φ ( i ) 2\varphi(i) 2φ(i) 个数对,所以结果为 2 ∑ i = 1 n φ ( i ) 2\sum_{i=1}^n\varphi(i) 2i=1nφ(i)

由于 φ ( i ) \varphi(i) φ(i) 可以在线性复杂度中求出,故时间复杂度可接受;

欧拉定理

若 n , a 为正整数,且 n , a 互质,则有

a φ ( n ) ≡ 1 ( m o d    n ) a^{\varphi(n)}\equiv 1(\mod n) aφ(n)1(modn)

费马小定理

费马小定理是欧拉定理的一种特殊情况

如果 p 是一个质数,而整数 a 不是 p 的倍数,则有

a p − 1 ≡ 1 ( mod  p ) a^{p-1}\equiv 1(\text{mod }p) ap11(mod p)

也可以表示成

a p ≡ a ( mod  p ) a^{p}\equiv a(\text{mod }p) apa(mod p)​​

欧拉降幂

由欧拉定理可推得在 a , n 互质时 a b ≡ a b % φ ( n ) ( m o d    n ) a^b\equiv a^{b\%\varphi(n)}(\mod n) abab%φ(n)(modn)
对于更广泛的情况,我们有

a b ≡ { a b % φ ( n ) g c d ( a , n ) = 1 a b g c d ( a , n ) ≠ 1 , b < φ ( n ) a b % φ ( n ) + φ ( n ) g c d ( a , n ) ≠ 1 , b ⩾ φ ( n ) m o d    n a^b\equiv\begin{cases} a^{b\%\varphi(n)}&gcd(a,n)=1\\ a^b&gcd(a,n)\not=1,b\lt\varphi(n)\\ a^{b\%\varphi(n)+\varphi(n)}&gcd(a,n)\not=1,b\geqslant\varphi(n) \end{cases} \mod n abab%φ(n)abab%φ(n)+φ(n)gcd(a,n)=1gcd(a,n)=1,b<φ(n)gcd(a,n)=1,bφ(n)modn

但是在实际操作中,并不需要判断第一种情况,直接将其归入情况二、三即可;

裴蜀定理 / 贝祖定理

若 a , b 是整数,且 g c d ( a , b ) = d gcd( a , b )=d gcd(a,b)=d ,那么对于任意的整数 x , y , a x + b y ax+by ax+by 均是 d 的倍数,特别地,一定存在整数 x , y,使 a x + b y = d ax+by=d ax+by=d 成立;

推论1:若 a x + b y = 1 ax+by=1 ax+by=1 有解,则有 g c d ( a , b ) = 1 gcd(a,b)=1 gcd(a,b)=1 ( a , b 互质);
推论2:若 a x + b y = m ax+by=m ax+by=m 有解,则 m 一定为 g c d ( a , b ) gcd(a,b) gcd(a,b) 的若干倍;

扩展欧几里得算法

证明过程同时也参考了这个帖子

扩展欧几里得算法是解决关于 x , y 的方程 a x + b y = c ax + by = c ax+by=c 的解的问题,如果 g c d ( a , b ) ∣ c gcd(a,b) |c gcd(a,b)c ,则有解(可能有多组),否则无解。

我们可以将这个问题归结为 求方程 a x + b y = g c d ( a , b ) ax + by = gcd(a,b) ax+by=gcd(a,b) 的解,若存在一组解 { x 0 , y 0 } \{x_0,y _0\} {x0,y0} ,则对于方程 a x + b y = k ∗ g c d ( a , b ) ax + by = k*gcd(a,b) ax+by=kgcd(a,b) ,解即为 { k x 0 , k y 0 } \{kx_0,ky_0\} {kx0,ky0}

若我们已经求得 { x 1 , y 1 } \{x_1,y_1\} {x1,y1} 满足 b x 1 + ( a % b ) y 1 = g c d ( b , a % b ) = d bx_1+(a\%b)y_1=gcd(b,a\%b)=d bx1+(a%b)y1=gcd(b,a%b)=d
上行方程左式可如此变形:
左式 = b x 1 + ( a − b ⌊ a b ⌋ ) y 1 = a y 1 + b ( x 1 − ⌊ a b ⌋ y 1 ) =bx_1+(a-b\lfloor \frac a b \rfloor)y_1=ay_1+b(x_1-\lfloor \frac a b \rfloor y_1) =bx1+(abba)y1=ay1+b(x1bay1)
所以此时有

{ x = y 1 y = x 1 − ⌊ a b ⌋ y 1 \begin{cases}x=y_1\\ y=x_1-\lfloor \frac a b \rfloor y_1 \end{cases} {x=y1y=x1bay1

由此,我们便可以进行递归回代处理;

对于递归终点,与求 gcd 时的类似,为 b = 0 b=0 b=0 ,此时有解 x = 1 , y = 0 x=1,y=0 x=1,y=0

代码
int exgcd(int a,int b,int &x,int &y)
{
   if(!b)
   {
      x=1,y=0;
      return a;
   }
   int ans=exgcd(b,a%b,x,y);
   int y1=y,x1=x;
   x=y1;
   y=x1-a/b*y1;
   return ans;
}

返回值即为 g c d ( a , b ) gcd(a,b) gcd(a,b)

逆元

即模意义下的倒数(除法)
若 x 满足 a x ≡ 1 ( mod  m ) ax\equiv 1(\text{mod } m) ax1(mod m) ,则称 x 是 a 在 m 意义下的逆元;
x 应满足 0 ⩽ x < m 0\leqslant x\lt m 0x<m

逆元的意义:
经验来讲,逆元一般是在做取模运算下的除法时会用到;

对于三个整数 a , b , M ,取模的性质有
( a + b ) % M = ( a % M + b % M ) % M (a+b)\%M=(a\%M+b\%M)\%M (a+b)%M=(a%M+b%M)%M
( a ∗ b ) % M = ( ( a % M ) ∗ ( b % M ) ) % M (a*b)\%M=((a\%M)*(b\%M))\%M (ab)%M=((a%M)(b%M))%M
对除法则没有此类性质;

若我们有 x 是 a 在 M 意义下的逆元,
则有 ( b / a ) % M = ( b ∗ x ) % M (b/a)\%M=(b*x)\%M (b/a)%M=(bx)%M

扩展欧几里得法求逆元

由上一部分可知,对于关于 x , y 的方程 A x + B y = g c d ( A , B ) Ax+By=gcd(A,B) Ax+By=gcd(A,B) 一定存在解;
将等式两边同时除以 g c d ( A , B ) gcd(A,B) gcd(A,B) ,化作 a x + b y = 1 ax+by=1 ax+by=1此时 g c d ( a , b ) = 1 gcd(a,b)=1 gcd(a,b)=1;(此有解结论同样可以由裴蜀定理得出)
再将等式两侧同时对 b 取模,化作 a x ≡ 1 ( mod  b ) ax\equiv 1(\text{mod } b) ax1(mod b)
此方程同样有一定有解;

接下来,我们应该将某特解 { x , y } \{x,y\} {x,y} ,转换为使得 x 满足条件且最小的解;

若有 { x , y } \{x,y\} {x,y} 满足 a x + b y = 1 ax+by=1 ax+by=1 ,同时即有 a ( x + b ) + b ( y − a ) = 1 a(x+b)+b(y-a)=1 a(x+b)+b(ya)=1
所以, { x + b , y − a } \{x+b,y-a\} {x+b,ya} 也是原方程的解;
由此类推 { x + k b , y − k a } \{x+kb,y-ka\} {x+kb,yka} 是原方程的通解;

所以,对于一组互质的 a, b 和特解 x , y ,则满足条件的最小 x 可以表示为 ( x % b + b ) % b (x\%b+b)\%b (x%b+b)%b

以上,对于给定的一组互质的 a , M ,a的逆元x为 exgcd(a,M,x,y),x=(x%M+M)%M;;
未证明:上面的exgcd板子代码中,返回的x若为正值,即为最小正数解

欧拉定理求逆元

PS:就以前的做题经验来看,个人几乎都是用这种方法求逆元
一般用于 M 为质数,且 a < M a<M a<M 时:

由于欧拉定理有

a φ ( M ) ≡ 1 ( mod  M ) a^{\varphi(M)}\equiv 1(\text{mod }M) aφ(M)1(mod M)

由于 M 为质数,则有

a M − 1 ≡ 1 ( mod  M ) a^{M-1}\equiv 1(\text{mod }M) aM11(mod M)
a ⋅ a M − 2 ≡ 1 ( mod  M ) a\cdot a^{M-2}\equiv 1(\text{mod }M) aaM21(mod M)

所以有 a M − 2 a^{M-2} aM2​​​​ 为 a 在 M 意义下的逆元;
具体的值可以由快速幂求出;

递归求逆元

求 a 在模 p 意义下的逆元即求 a − 1 a^{-1} a1 在模 p 意义下的值;

p = k a + r   k a + r ≡ 0 ( m o d    p )   k r − 1 + a − 1 ≡ 0 ( m o d    p )   a − 1 ≡ − k r − 1 ( m o d    p )   a − 1 ≡ − ⌊ p / a ⌋ ⋅ i n v ( p % a ) ( m o d    p ) p=ka+r\\\text{ }\\ ka+r\equiv0(\mod p)\\\text{ }\\ kr^{-1}+a^{-1}\equiv0(\mod p)\\\text{ }\\ a^{-1}\equiv-kr^{-1}(\mod p)\\\text{ }\\ a^{-1}\equiv-\lfloor p/a\rfloor\cdotp inv(p\%a)(\mod p) p=ka+r ka+r0(modp) kr1+a10(modp) a1kr1(modp) a1p/ainv(p%a)(modp)

由此我们可以递归实现求取

long long inv(long long a)
{
    if(a==1)return 1;
    return (mod-mod/a)*inv(mod%a)%mod;
}

代码中多加入了防负的部分

线性求逆元

依据上面的公式,同样可以得出线性求逆元的代码:

ll inv[N+5];
void ginv()
{
	inv[1]=1;
	for(int i=2;i<=N;i++)
		inv[i]=(M-M/i)*inv[M%i]%M;
}

线性求逆元主要用于需要使用大量数的逆元时;

需要注意的是,虽然 i n v [ ] inv[] inv[] 数组中每个下标都有值,但不是每个下标的值都是其逆元,有些数是没有模M意义下的逆元的;

莫比乌斯函数

莫比乌斯函数定义有

μ ( n ) { 1 n = 1 ( − 1 ) k n 无 平 方 因 数 , 且 n = p 1 p 2 . . . p k 0 n 有 大 于 1 的 平 方 因 数 \mu(n)\begin{cases} 1&n=1\\ (-1)^k&n无平方因数,且n=p_1p_2...p_k\\ 0&n有大于1的平方因数 \end{cases} μ(n)1(1)k0n=1nn=p1p2...pkn1

与欧拉函数类似,莫比乌斯函数同样为积性函数:
若 a , b 互质,则有

μ ( a ⋅ b ) = μ ( a ) ⋅ μ ( b ) \mu(a\cdotp b)=\mu(a)\cdotp\mu(b) μ(ab)=μ(a)μ(b)

还有如下性质

∑ d ∣ n μ ( d ) = [ n = 1 ] \sum_{d|n}\mu(d)=[n=1] dnμ(d)=[n=1]​​​​​

代码

与欧拉筛共同完成:

mu[1]= 1;
for(int i = 2 ; i < N ; i ++ )
{
    if(!n[i])
    {
        prime[c++] = i;
        mu[i] = -1;
    }
    for(int j = 0 ; j < c && (ll)i * prime[j] < N ; j ++ )
    {
        n[i * prime[j] ] = 1;
        if(i % prime[j] == 0)
        {
            mu[i * prime[j]] = 0;//不互质,存在平方因子
            break;
        }
        mu[i * prime[j]] = -mu[i];//互质,质数的莫比乌斯函数值为1
    }
 }
莫比乌斯反演

此部分参考了这个视频

设两函数 F ( n ) , f ( n ) F(n),f(n) F(n),f(n) 有以下两种形式

F ( n ) = ∑ d ∣ n f ( n ) ⇒ f ( n ) = ∑ d ∣ n μ ( d ) F ( n d )   F ( n ) = ∑ n ∣ d f ( n ) ⇒ f ( n ) = ∑ n ∣ d μ ( d n ) F ( d ) F(n)=\sum_{d|n}f(n)\Rightarrow f(n)=\sum_{d|n}\mu(d)F(\frac n d)\\\text{ }\\ F(n)=\sum_{n|d}f(n)\Rightarrow f(n)=\sum_{n|d}\mu(\frac d n)F(d) F(n)=dnf(n)f(n)=dnμ(d)F(dn) F(n)=ndf(n)f(n)=ndμ(nd)F(d)

例题

此部分参考了这篇文章

∑ i = 1 n ∑ j = 1 m [ g c d ( i , j ) = 1 ] , n ⩽ 1 e 7 \sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)=1],n\leqslant1e7 i=1nj=1m[gcd(i,j)=1],n1e7 ;

由于 ∑ d ∣ n μ ( d ) = [ n = 1 ] \sum_{d|n}\mu(d)=[n=1] dnμ(d)=[n=1] ,将 n 换为 g c d ( i , j ) gcd(i,j) gcd(i,j) ,即有 ∑ d ∣ g c d ( i , j ) μ ( d ) = [ g c d ( i , j ) = 1 ] \sum_{d|gcd(i,j)}\mu(d)=[gcd(i,j)=1] dgcd(i,j)μ(d)=[gcd(i,j)=1]

将这个结论代入题给式,即为 ∑ i = 1 n ∑ j = 1 m ∑ d ∣ g c d ( i , j ) μ ( d ) \sum_{i=1}^n\sum_{j=1}^m\sum_{d|gcd(i,j)}\mu(d) i=1nj=1mdgcd(i,j)μ(d)
[ 1 , n ] [1,n] [1,n] 区间中,有 d , 2 d , 3 d , . . . , ⌊ n d ⌋ d d,2d,3d,...,\lfloor\frac n d\rfloor d d,2d,3d,...,dnd ,共 ⌊ n d ⌋ \lfloor\frac n d\rfloor dn 个 d 的倍数;
[ 1 , m ] [1,m] [1,m] 区间中,有 d , 2 d , 3 d , . . . , ⌊ m d ⌋ d d,2d,3d,...,\lfloor\frac m d\rfloor d d,2d,3d,...,dmd ,共 ⌊ m d ⌋ \lfloor\frac m d\rfloor dm 个 d 的倍数;
两个集合中的 d 的整数倍数,两两的最大公约数均为 d 或 d 的倍数;
显然 d ∈ [ 1 , m i n ( n , m ) ] d\in[1,min(n,m)] d[1,min(n,m)]
所以上式有以下变形
∑ d = 1 m i n ( n , m ) μ ( d ) ⌊ n d ⌋ ⌊ m d ⌋ \sum_{d=1}^{min(n,m)}\mu(d)\lfloor\frac n d\rfloor\lfloor\frac m d\rfloor d=1min(n,m)μ(d)dndm
然后就可以通过整除分块优化对 d 的枚举了;

鸽巢原理

N 只鸽子放在 N-1 个鸽巢中的话,必存在多只鸽子在一个巢;

应用

例如:

f ( 1 ) = 1 , f ( 2 ) = 2 , f ( n ) = ( A f ( n − 1 ) + B f ( n − 2 ) ) % 7 f(1)=1,f(2)=2,f(n)=(Af(n-1)+Bf(n-2))\%7 f(1)=1,f(2)=2,f(n)=(Af(n1)+Bf(n2))%7 ,给定 n ,求 f ( n ) f(n) f(n)

这个问题中,如果存在 x , y x,y x,y 满足 x > y x>y x>y { f ( y − 1 ) , f ( y ) } = { f ( x − 1 ) , f ( x ) } \{f(y-1),f(y)\}=\{f(x-1),f(x)\} {f(y1),f(y)}={f(x1),f(x)} ,这个数列即出现了循环;

关于循环节长度, { f ( x − 1 ) , f ( x ) } \{f(x-1),f(x)\} {f(x1),f(x)} 一共有 7 2 = 49 7^2=49 72=49 种情况,即50项以内必出循环;

推广来说,对于需要前 n 项的递推方程,结果模 m ,那么循环节长度至多为 n ⋅ m n\cdotp m nm

二进制操作

bitcount

返回给定数字中有多少二进制1;

O ( 1 ) O(1) O(1)参考与原理解释

int bitcount(int i)
{
       i = i - ((i >> 1) & 0x55555555);
       i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
       i = (i + (i >> 4)) & 0x0f0f0f0f;
       i = i + (i >> 8);
       i = i + (i >> 16);
       return i & 0x3f;
}

O ( l o g ( n ) ) O(log(n)) O(log(n))

int bitcount(int x){
	return x==0?0:bitcount(x/2)+(x&1);
}
lowbit

返回给定数字中最低位1代表的十进制数;

一般用在树状数组中;

int lowbit(int x){return x&-x;}

ED

\

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值