倍增法(英语:binary lifting),顾名思义就是「成倍增长」。我们在进行递推时,如果状态空间很大,通常的线性递推无法满足时间与空间复杂度的要求,那么我们可以通过成倍增长的方式,只递推状态空间中在 k 的整数次幂位置上的值作为代表。当需要其他位置上的值时,我们通过「任意整数可以表示成若干个 k 的次幂项的和」这一性质,使用之前求出的代表值拼成所需的值。所以使用倍增算法也要求我们递推的问题的状态空间关于 k 的次幂具有可划分性。通常情况下 k 取 2。1
#include <cstdio>using namespace std;constexpr int mod = 1000000007;int modadd(int a, int b) { if (a + b >= mod) return a + b - mod; // 减法代替取模,加快运算 return a + b;}int vi[1000005];int go[75][1000005]; // 将数组稍微开大以避免越界,小的一维尽量定义在前面int sum[75][1000005];int main() { int n, k; scanf("%d%d", &n, &k); for (int i = 1; i <= n; ++i) { scanf("%d", vi + i); } for (int i = 1; i <= n; ++i) { go[0][i] = (i + k) % n + 1; sum[0][i] = vi[i]; } int logn = 31 - __builtin_clz(n); // 一个快捷的取对数的方法 for (int i = 1; i <= logn; ++i) { for (int j = 1; j <= n; ++j) { go[i][j] = go[i - 1][go[i - 1][j]]; sum[i][j] = modadd(sum[i - 1][j], sum[i - 1][go[i - 1][j]]); } } long long m; scanf("%lld", &m); int ans = 0; int curx = 1; for (int i = 0; m; ++i) { if (m & (1ll << i)) { // 参见位运算的相关内容,意为 m 的第 i 位是否为 1 ans = modadd(ans, sum[i][curx]); curx = go[i][curx]; m ^= 1ll << i; // 将第 i 位置零 } } printf("%d\n", ans);}