简单数学和位运算
整除和余数
- 以下 x、y、a 均为整数。
- 存在整数 k,使得 x=ky,则有 y∣x( y 整除 x ),若 y>0 则称 x 是 y 的倍数,y 是 x 的因数。
- 如果 x=ky+a,则称 a 为 x/y 的余数,写作 a=x,当 a=0 当且仅当 y∣x。
同余
- 如果 x%z=y%z ,则我们称 x 和 y 对 z 同余,记作 x≡y(modz)。
- 如果 (x−y) 是 z 的倍数,那么 x 和 y 对 z 同余。
证明:记 x=cz+a,y=dz+b(0≤a,b<z) 则 x−y=(c−d)z+a−b,由于 (x−y) 是 z 的倍数,所以 (a−b)∣z,而 0≤a,b<z,故易得 a=b,所以同余。
- 如果且 a≡b(modm),c≡d(modm) ,则 a±c≡b±d(modz),证明雷同上面。
- 乘号同理,但是除法不行。
- 如果 a≡b(modm),则 an≡bn(modm)。
取余运算
- (a+b)%m=(a%m+b%m)%m
- (a−b)%m=(a%m−b%m+m)%m
- (a×b)%m=(a%m×b%m)
- ab%m=(a%m)b%m
- 这些等式可以用在需要取模,且数字会经过多次运算的题目中,防止中间变量超出数据范围。
质数
- 只能被 1 和自己整除的数,称为质数
- 当 N 充分大时,1 ~ N 范围内的质数个数约为 N/logN。
- 在某个正整数 N 附近找质数,能够在 logN 期望时间内发现一个质数。
- 如何判断一个整数 n 是否为质数:2 到 n 试除,发现约数则不是质数,复杂度 O(n)
- a 和 n/a 中至少有 1 个数不超过 n,完全平方数的情况 2 个数都等于 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×i 开始是因为 2×i、3×i 等数字在 i=2,i=3 的时候已经被枚举过了。
- 时间复杂度 O(nloglogn)。
筛法(线性筛)
- 数字 30 还是被筛了多次(3×10、2×15、5×6)。如果能保证每个合数都只被它的最小质因数筛掉就好了。
比如 2 筛掉 4、6、8、10、12⋯
比如 3 筛掉 9、15、21⋯
比如 5 筛掉 25、35、55⋯
- 我们记求解到 i 的质数数组为 p,则对于每个数字 i,我们希望 i×p1、i×p2 等数字都被筛掉。这样一定是能筛干净的,因为合数 a= 某个质数 b× 另一个数 c,我们一定能在枚举到每个合数前,枚举到比它小的 c,这样当 pj=b 时,a 就被干掉了。
- 每个合数还是没有只被它的最小质因数筛。
- 如 60,被 3×20 干掉一次,被 15×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× p[k]
的最小质因数肯定是 p[k]
。
- 同理可证,设有比
p[j]
大的质数 p[l]
,则删 p[l]
×i 一定会出现重复,因为假设 p[l]
×i/ p[j]
是 x,当 i 枚举到 x 时候,x× 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=p1e1×p2e2⋯×ptet
- 其中 pi 是质数,ei 是正整数,且 i 和 ∑ei都是 logn 数量级别的。
- 根据乘法定理易得 n 的因数个数为 (e1+1)×(e2+1)×⋯×(et+1)。
- 算术基本定理是一条非常基本和重要的定理,它把对自然数的研究转化为对其最基本的元素——质数的研究。
质因数分解
- 2 到 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 的唯一分解中任意质数的指数 ei 为 x,y 的唯一分解中此质数指数的 min。
- 设 e=lcm(x,y),那么 e 的唯一分解中任意质数的指数 ei为 x,y 的唯一分解中此质数指数的 max。
- 比如 gcd(240,180)=60,分解质因数如下,发现 60 的质因数中 2 和3的指数取得是上面两个数分解 2 和 3 的指数的最小值
- 180=22×32×51
- 240=24×31×51
- 60=22×31×51
- lcm 则是取最大值,易得 gcd(x,y)×lcm(x,y)=x×y
位运算
- & 运算:二进制下进行二元运算,每一位全 1 则 1 ,反之为 0。性质:a&b≤min(a,b).
- ∣ 运算:二进制下进行二元运算,每一位有 1 则1,反之为 0。性质:a∣b≥max(a,b).
- ^ 运算:二进制下进行二元运算,每一位不同则 1,反之为 0。性质是自己就是自己的逆运算。
- 运算:一元运算符(和符号一样),让二进制下的每一位都取反(包括符号位)。
- <<运算:进制下把每一位向左平移,低位用 0 补足。
- 1<<n 等价于 2n,x<<n 等价于 x×2n
- >>运算:进制下把每一位向右平移,低位越界则舍弃。
- n>>1=⌊2n⌋
- 注意位运算优先级很低,搞不清就打括号!
一些其他操作
- lowbit(n) 表示表示二进制下 n 的最低的一个 1 它后面的 0 组成的数.
- lowbit(n)=n&−n,原理自己想。
- popcount(n) 表示二进制 n 有多少位是 1,原理自己想。
int ans=0;
while(n)
{
ans++;
n-=lowbit(n);
}
return ans;
一些其他的其他操作
- 检测第 k 位是 0/1:
(n&(1<<(k-1)))!=0
→1
- 给第 k 位取反:
n^=(1<<(k-1))
- 给第 k 为赋值为 1:
n|=(1<<(k-1))
- 给第 k 为赋值为 0:
n&=(~1<<(k-1))