快速幂
例子:求 a b m o d m a^b \ mod \ m ab mod m的值。
如
a
7
a^7
a7 7 二进制为(111)
a
7
a^7
a7 =
a
4
a^4
a4 *
a
2
a^2
a2 *
a
1
a^1
a1
a
18
a^{18}
a18 18 二进制为(10010)
a
18
a^{18}
a18 =
a
16
a^{16}
a16 *
a
2
a^2
a2
a的任意次幂都可以由多个不同的a的
2
k
2^k
2k次幂相乘得到,可以通过对a不断平方并把结果赋值给a,依次得到
a
2
0
a^{2^0}
a20 (a),
a
2
1
a^{2^1}
a21(
a
2
a^2
a2 = a * a),
a
2
2
a^{2^2}
a22(
a
4
a^4
a4 =
a
2
a^2
a2 *
a
2
a^2
a2), ……
a
2
k
a^{2^k}
a2k,即通过不断地进行 a = a * a;
而 a b a^b ab 𝑚𝑜𝑑 𝑚 = ( a k a^k ak 𝑚𝑜𝑑 𝑚 )* ( a t a^t at 𝑚𝑜𝑑 𝑚 )(k + t = b),因此求 a b a^b ab 𝑚𝑜𝑑 𝑚 的值可由以下代码实现
int quick_pow (int a, int b)
{
int res = 1;
while (b)
{
if (b&1)
res = res * a % mod;
a = a*a % mod;
b >>= 1;
}
return res;
}
以b = 5为例来看上面代码,5二进制为(101),“&”为按位与运算
b = 101,b&1 = 1; res = a; a = a*a;
b = 10,b&1 = 0; (res = res * a % mod;不执行),res = a;a =
a
2
a^2
a2 *
a
2
a^2
a2
b = 1,b&1 = 1;res = a *
a
4
a^4
a4 ;a =
a
4
a^4
a4 *
a
4
a^4
a4
b = 0,退出循环
res的值即为
a
5
a^5
a5 。
应用
快速幂可以用来计算斐波那契数列。
已知斐波那契数列
F
n
F_n
Fn =
F
n
−
1
F_{n−1}
Fn−1 +
F
n
−
2
F_{n−2}
Fn−2 (n>=3),
F
1
F_1
F1 = 1,
F
2
F_2
F2 = 1
由斐波那契数列定义可得
只需利用快速幂计算
[
1
1
1
0
]
n
−
1
\begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix}^{n-1} \quad
[1110]n−1再与
[
f
(
2
)
f
(
1
)
]
\begin{bmatrix} f(2) \\ f(1) \end{bmatrix} \quad
[f(2)f(1)] 相乘即可得出f(n)。
关于取整的经典题目
∑ k = 1 n ⌊ n k ⌋ \sum_{k = 1}^n⌊\frac n k⌋ ∑k=1n⌊kn⌋,n <= 1 0 9 10^9 109
容易发现使得
⌊
n
k
⌋
⌊\frac n k⌋
⌊kn⌋每种取值的k一定是连续的一段区间。
例如,使10/k值为1 的 k 在连续区间[6,10]。
因此,只要知道每种不同取值k的区间长度,把不同n/k的值乘以区间长度,再相加,即可得到结果。
左端点 l 从1开始,易知,每次新区间的左端点等于上一个区间的右端点加1
x = n / l,右端点 r 是满足n / r = x的最大正整数, n 除以r 向下取整为x,所以n >= x * r,只有当 r = n / x 向下取整时,r才为最大。
所以r = n / x = n / (n / l);
int count (int n)
{
int ans = 0;
for (int l = 1, r = 1; l <= n; l = r + 1)
{
r = n / (n / l);
ans += n / l * (r - l + 1);
}
return ans;
}
取整与取模的转换
𝑛 𝑚𝑜𝑑 𝑚 = 𝑛 −𝑚 ⌊ n m ⌋ ⌊\frac n m⌋ ⌊mn⌋
求 ∑ k = 1 n n m o d k \sum_{k = 1}^n n\mod\ k ∑k=1nnmod k,n <= 1 0 9 10^9 109
∑ k = 1 n n m o d k \sum_{k = 1}^n n\mod\ k ∑k=1nnmod k = ∑ k = 1 n n − k ∗ ⌊ n k ⌋ \sum_{k = 1}^n n-k*⌊\frac n k⌋ ∑k=1nn−k∗⌊kn⌋ =n * n - ∑ k = 1 n k ∗ ⌊ n k ⌋ \sum_{k = 1}^n k*⌊\frac n k⌋ ∑k=1nk∗⌊kn⌋
# include <iostream>
using namespace std;
int count (int n)
{
int ans = 0;
for (int l = 1, r = 1; l <= n; l = r + 1)
{
r = n / (n / l);
ans += (l+r)*(r-l+1)/2*(n/l);
}
return ans;
}
int main ()
{
int n;
cin >> n;
cout <<n*n - count(n);
return 0;
}
对每个区间[l,r], ⌊ n k ⌋ ⌊\frac n k⌋ ⌊kn⌋ = n / l,是个固定值
∑ k = l r k ∗ ⌊ n k ⌋ \sum_{k = l}^r k*⌊\frac n k⌋ ∑k=lrk∗⌊kn⌋ = ⌊ n k ⌋ ⌊\frac n k⌋ ⌊kn⌋ * ∑ k = l r k \sum_{k = l}^r k ∑k=lrk = ⌊ n k ⌋ ⌊\frac n k⌋ ⌊kn⌋ * (l+r) * (r-l+1)/2 = (n/l) * (l+r)*(r-l+1)/2
由此,可以求出每个区间 ∑ k = l r k ∗ ⌊ n k ⌋ \sum_{k = l}^r k*⌊\frac n k⌋ ∑k=lrk∗⌊kn⌋的值,从而得到 ∑ k = 1 n k ∗ ⌊ n k ⌋ \sum_{k = 1}^n k*⌊\frac n k⌋ ∑k=1nk∗⌊kn⌋的值。
由 ∑ k = 1 n n m o d k \sum_{k = 1}^n n\mod\ k ∑k=1nnmod k = n * n - ∑ k = 1 n k ∗ ⌊ n k ⌋ \sum_{k = 1}^n k*⌊\frac n k⌋ ∑k=1nk∗⌊kn⌋可以求出最终结果