简单数学与位运算

本文经 @sLMxf 转载:

整除和余数

  • 以下 \(x、y、a\) 均为整数。
  • 存在整数 \(k\),使得 \(x = ky\),则有 \(y|x\)\(y\) 整除 \(x\) ),若 \(y>0\) 则称 \(x\)\(y\) 的倍数,\(y\)\(x\) 的因数。
  • 如果 \(x = ky + a\),则称 \(a\)\(x/y\) 的余数,写作 \(a = x % y\),当 \(a = 0\) 当且仅当 \(y|x\)

同余

  • 如果 \(x \% z = y \% z\) ,则我们称 \(x\)\(y\)\(z\) 同余,记作 \(x \equiv y \pmod z\)
  • 如果 \((x − y)\)\(z\) 的倍数,那么 \(x\)\(y\)\(z\) 同余。
    证明:记 $x = cz + a,y = dz + b(0 \le a,b < z) $ 则 \(x − y = (c − d)z + a − b\),由于 \((x − y)\)\(z\) 的倍数,所以 \((a − b)|z\),而 \(0 \le a,b < z\),故易得 \(a = b\),所以同余。
  • 如果且 \(a \equiv b \pmod m,c \equiv d \pmod m\) ,则 \(a\pm c \equiv b\pm d \pmod z\),证明雷同上面。
  • 乘号同理,但是除法不行。
  • 如果 \(a \equiv b \pmod m\),则 \(a^n \equiv b^n\pmod m\)

取余运算

  • \((a + b) \% m = (a \% m + b \% m) \% m\)
  • \((a − b) \% m = (a \% m − b \% m \color{red}{+ m}\color{black}) \% m\)
  • \((a \times b) \% m = (a \% m \times b \% m) % m\)
  • \(a^b \% m = (a \% m)^b \% m\)
  • 这些等式可以用在需要取模,且数字会经过多次运算的题目中,防止中间变量超出数据范围。

质数

  • 只能被 \(1\) 和自己整除的数,称为质数
  • \(N\) 充分大时,\(1\) ~ \(N\) 范围内的质数个数约为 \(N/\log N\)
  • 在某个正整数 \(N\) 附近找质数,能够在 \(\log N\) 期望时间内发现一个质数。
  • 如何判断一个整数 \(n\) 是否为质数:\(2\)\(\sqrt{n}\) 试除,发现约数则不是质数,复杂度 \(O(\sqrt{n})\)
  • \(a\)\(n/a\) 中至少有 \(1\) 个数不超过 \(\sqrt{n}\),完全平方数的情况 \(2\) 个数都等于 \(\sqrt{n}\)

筛法(埃式筛)

  • 在学数组的时候,我们学过一个判断 \(1\) ~ \(n\) 是否为质数的方法:从小到大,依次将每个质数的倍数删去(若遍历到一个数时,其没有被删去,则可知这个数是质数),最终留下的全是质数。
f[1]=false;
for(int i=2;i<=n;i++) f[i]=true;
for(int i=2;i<=n;i++)
{
        if(f[i]==true)
            for(long long j=(long long)i*i;j<=n;j+=i)
                f[j]=false;
}
  • \(j\)\(i\times i\) 开始是因为 \(2\times i\)\(3\times i\) 等数字在 \(i=2,i=3\) 的时候已经被枚举过了。
  • 时间复杂度 \(O(n\log\log n)\)

筛法(线性筛)

  • 数字 \(30\) 还是被筛了多次(\(3\times 10、2\times15、5\times 6\))。如果能保证每个合数都只被它的最小质因数筛掉就好了。
    比如 \(2\) 筛掉 \(4、6、8、10、12\cdots\)
    比如 \(3\) 筛掉 \(9、15、21\cdots\)
    比如 \(5\) 筛掉 \(25、35、55\cdots\)
  • 我们记求解到 \(i\) 的质数数组为 \(p\),则对于每个数字 \(i\),我们希望 \(i\times p_1、i\times p_2\) 等数字都被筛掉。这样一定是能筛干净的,因为合数 \(a=\) 某个质数 \(b\times\) 另一个数 \(c\),我们一定能在枚举到每个合数前,枚举到比它小的 \(c\),这样当 \(p_j=b\) 时,\(a\) 就被干掉了。
  • 每个合数还是没有只被它的最小质因数筛。
  • \(60\),被 \(3\times 20\)干掉一次,被 \(15\times 4\)干掉一次。
f[1]=false;
for(int i=2;i<=n;i++) f[i]=true;
for(int i=2;i<=n;i++)
{
        if(f[i]==true) p[cnt++]=i;
        for(int j=1;j<=cnt&&i*p[j]<=n;j++)
            f[p[j]*i]=false;
}
  • 内层循环加上一句if(i%p[j]==0)break;即可。为什么?
  • 此时,比 p[j] 小的质数 p[k] 不满足 p[k] \(|i\),且 \(i\) 的所有质因数肯定都比 p[j] 大,因为如果有小的那 \(j\) 早就被停在前面了。故被筛掉的 \(i\times\) p[k] 的最小质因数肯定是 p[k]
  • 同理可证,设有比 p[j] 大的质数 p[l] ,则删 p[l] \(\times i\) 一定会出现重复,因为假设 p[l] \(\times i /\) p[j]\(x\),当 \(i\) 枚举到 \(x\) 时候,\(x\times\) p[j] 又会把这个数字筛一次。
  • 时间复杂度 \(O(n)\)
f[1]=false;
for(int i=2;i<=n;i++) f[i]=true;
for(int i=2;i<=n;i++)
{
        if(f[i]==true) p[cnt++]=i;
        for(int j=1;j<=cnt&&i*p[j]<=n;j++)
        { 
            f[p[j]*i]=false;
            if(i%p[j]==0)break;
        }
}

唯一分解定理

  • 任何大于1的正整数n仅能以唯一方式分解为有限质数的乘积:
  • \(n = p_1^{e_1} \times p_2^{e_2} \cdots \times p_t^{e_t}\)
  • 其中 \(p_i\) 是质数,\(e_i\) 是正整数,且 \(i\)\(\sum e_i\)都是 \(\log n\) 数量级别的。
  • 根据乘法定理易得 \(n\) 的因数个数为 \((e_1+1)\times (e_2+1)\times\cdots\times(e_t+1)\)
  • 还能求出 \(n\) 的因数和为 \((p_1^{e_1}+p_1^{e_1-1}+p_1^{e_1-2}+\cdots +p_1)(p_2^{e_2}+p_2^{e_2-1}+p_2^{e_2-2}+\cdots +p_2)\cdots (p_n^{e_n}+p_n^{e_n-1}+p_n^{e_n-2}+\cdots +p_n)\)
  • 算术基本定理是一条非常基本和重要的定理,它把对自然数的研究转化为对其最基本的元素——质数的研究。

质因数分解

  • \(2\)\(\sqrt{n}\) 都试一下,如果能除的尽 \(n\) 就用 while 除,要把次数记下来。
  • 最后剩下的数字如果步数 \(1\) ,一定是最大的质因数。

gcd 和 lcm

  • \(m|x\)\(m|y\) ,称为 \(m\)\(x\)\(y\) 的公因数。最大的 \(m\) 叫最大公因数 \((gcd)\)
  • \(x|n\)\(y|n\),称为 \(n\)\(x\)\(y\) 的公倍数。最小的 \(n\) 叫最小公倍数 \((lcm)\)
  • \(d = \gcd(x,y)\),那么 \(d\) 的唯一分解中任意质数的指数 \(e_i\)\(x,y\) 的唯一分解中此质数指数的 \(\min\)
  • \(e = lcm(x,y)\),那么 \(e\) 的唯一分解中任意质数的指数 \(e_i\)\(x,y\) 的唯一分解中此质数指数的 \(\max\)
  • 比如 \(\gcd(240,180)=60\),分解质因数如下,发现 \(60\) 的质因数中 \(2\)\(3\)的指数取得是上面两个数分解 \(2\)\(3\) 的指数的最小值
    • \(180 = 2^2 \times 3^2 \times 5^1\)
    • \(240 = 2^4 \times 3^1 \times 5^1\)
    • \(60 = 2^2 \times 3^1 \times 5^1\)
  • \(lcm\) 则是取最大值,易得 \(\gcd(x,y) \times lcm(x,y) = x \times y\)

位运算

  • \(\&\) 运算:二进制下进行二元运算,每一位全 \(1\)\(1\) ,反之为 \(0\)。性质:\(a\&b\le \min(a,b)\).
  • \(|\) 运算:二进制下进行二元运算,每一位有 \(1\) 则1,反之为 \(0\)。性质:\(a|b\ge \max(a,b)\).
  • ^ 运算:二进制下进行二元运算,每一位不同则 \(1\),反之为 \(0\)。性质是自己就是自己的逆运算。
  • \(\sim\) 运算:一元运算符(和符号一样),让二进制下的每一位都取反(包括符号位)。
  • \(<<\)运算:进制下把每一位向左平移,低位用 \(0\) 补足。
  • \(1<<n\) 等价于 \(2n\)\(x<<n\) 等价于 \(x \times 2n\)
  • \(>>\)运算:进制下把每一位向右平移,低位越界则舍弃。
  • \(n>>1=\lfloor\frac{n}{2}\rfloor\)
  • 注意位运算\(\color{red}优先级很低\),搞不清就打括号!

一些操作

  • \(lowbit(n)\) 表示表示二进制下 \(n\) 的最低的一个 \(1\) 它后面的 \(0\) 组成的数.
  • \(lowbit(n)=(n)\text{ and }(-n)\),原理自己想。
  • \(popcount(n)\) 表示二进制 \(n\) 有多少位是 \(1\),原理自己想。
int ans=0;
while(n)
{
    ans++;
    n-=lowbit(n);
}
return ans;
posted @ 2024-07-18 10:13  GCSG01  阅读(28)  评论(0)    收藏  举报