专栏文章
万能欧几里得算法
算法·理论参与者 3已保存评论 2
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 1 条
- 当前快照
- 2 份
- 快照标识符
- @mikjjl98
- 此快照首次捕获于
- 2025/11/30 01:04 3 个月前
- 此快照最后确认于
- 2025/12/02 01:09 3 个月前
类欧几里得算法
类欧几里得算法常用于解决形如 ()的问题。
代数推导,当 从 求和到 的答案为 :
-
,变为 。
-
考虑交换求和顺序,令 :对于 ,它等于 。则有 ,变为 ,可以得到 。因此 可以变为 。
这一维是单调减的, 这一维每两步至少除以二,因此时间复杂度是 的。
它的几何意义:
- 对第一步是把斜率大于等于 的都减去 。
- 对第二步则是把横纵坐标反转。
万能欧几里得算法
我们对类欧几里得算法进行了扩展,考虑一下它的几何意义。
考虑用折线来刻画一下这个求值。我们的直线每穿过一条横线的时候记为 ,穿过一条竖线记为 。特别地,当它同时穿过横线和竖线的时候先 后 。我们要求这个操作序列最后的位置为 。
图例为 :

此时 轴为 ,我们维护 和 的值。当 时, 会加 次(向上走 步),而 会加上 。我们使用矩阵维护(),则每次向上的时候让将其乘上 ,向右乘上 。令 为上述问题的答案(即有 个 ,第 个 前有 个 ,最后一个是 的答案),则:
-
当 的时候,答案为 。
-
当 的时候,答案为 。即我们变为

-
此时 ,我们关于 对称,即我们对每个 统计其之前的 的个数(这样才能变为一个类似于欧几里得算法的形式)。(几何意义:对于整点的特殊情况,我们平移 解决)

-
对于第 个 ,求 。对 ,有之前的类似转化,变为:因此第 个 前会有 个 ,令 。我们可以把 变为 。但是 是负数,我们初始要求 。这一个可以直接把 的单独计算来解决。还有一个问题是我们要求最后一个是 (即 ),但是最后若干个变为了 ()。因此我们也需要提出最后一个连续段。在提出 后,第 个 前应有 。最后应有 个 。即 。特别地,由于我们需要掐头去尾,我们特判 的情况,此时答案为 。
练习1:P5170 【模板】类欧几里德算法
Problem
多组询问,每次给定 。
求 ,,。
Sol
令 。需要维护 的值。
遇到 时,。
遇到 时,,,,,。
考虑合并两个区间的信息。则有 ,,,,,。
Code
CPP#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int P = 998244353;
struct Node {
ll x, y, sx, sy, sy2, sxy;
Node() : x(0), y(0), sx(0), sy(0), sy2(0), sxy(0) {}
Node(ll _x, ll _y, ll _sx, ll _sy, ll _sy2, ll _sxy) : x(_x), y(_y), sx(_sx), sy(_sy), sy2(_sy2), sxy(_sxy) {}
friend Node operator*(const Node &l, const Node &r) {
Node res;
res.x = (l.x + r.x) % P;
res.y = (l.y + r.y) % P;
res.sx = (l.sx + r.sx + l.x * r.x) % P;
res.sy = (l.sy + r.sy + l.y * r.x) % P;
res.sy2 = (l.sy2 + r.sy2 + r.x * l.y % P * l.y + 2 * l.y * r.sy) % P;
res.sxy = (l.sxy + r.sxy + r.x * l.x % P * l.y % P + l.x * r.sy + l.y * r.sx) % P;
return res;
}
};
Node QPow(Node a, ll b) {
Node res;
for (; b; b >>= 1, a = a * a)
if (b & 1)
res = res * a;
return res;
}
Node F(ll n, ll a, ll b, ll c, Node U, Node R) {
if (!n) return Node();
if (b >= c) return QPow(U, b / c) * F(n, a, b % c, c, U, R);
if (a >= c) return F(n, a % c, b, c, U, QPow(U, a / c) * R);
ll m = (a * n + b) / c;
if (!m) return QPow(R, n);
return QPow(R, (c - b - 1) / a) * U * F(m - 1, c, (c - b - 1) % a, a, R, U) * QPow(R, n - (c * m - b - 1) / a);
}
int main() {
Node U, R;
U.y = 1;
R.x = 1, R.sx = 1;
int T;
scanf("%d", &T);
while (T--) {
ll n, a, b, c;
scanf("%lld%lld%lld%lld", &n, &a, &b, &c);
Node ret = F(n, a, b, c, U, R);
ret.sy += b / c;
ret.sy2 += (b / c) * (b / c);
printf("%lld %lld %lld\n", ret.sy % P, ret.sy2 % P, ret.sxy);
}
return 0;
}
练习2:
Problem
求:
其中 是 行 列的矩阵。。
我们维护 。
遇到 时:。
遇到 时:,。
考虑合并左右区间的信息。有 ,,,这个是因为 都是 的次幂,所以能交换。
Code
CPP#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef __int128_t i128;
const int P = 998244353;
struct Matrix {
int n, m;
ll a[25][25];
Matrix() : n(0), m(0) { memset(a, 0, sizeof (a)); }
Matrix(int _n) : n(_n), m(_n) {
memset(a, 0, sizeof (a));
for (int i = 1; i <= n; i++) a[i][i] = 1;
}
Matrix(int _n, int _m) : n(_n), m(_m) { memset(a, 0, sizeof (a)); }
ll *operator[](int x) { return a[x]; }
const ll *operator[](int x) const { return a[x]; }
friend Matrix operator+(const Matrix &a, const Matrix &b) {
Matrix c(a.n, a.m);
for (int i = 1; i <= a.n; i++)
for (int j = 1; j <= a.m; j++)
c[i][j] = (a[i][j] + b[i][j]) % P;
return c;
}
friend Matrix operator*(const Matrix &a, const Matrix &b) {
Matrix c(a.n, b.m);
for (int i = 1; i <= a.n; i++)
for (int k = 1; k <= a.m; k++)
for (int j = 1; j <= b.m; j++)
(c[i][j] += a[i][k] * b[k][j]) %= P;
return c;
}
};
int m;
struct Node {
Matrix x, y, s;
Node() : x(m), y(m), s(m, m) {}
friend Node operator*(const Node &l, const Node &r) {
Node res;
res.x = l.x * r.x;
res.y = l.y * r.y;
res.s = l.s + l.x * r.s * l.y;
return res;
}
};
Node QPow(Node a, ll b) {
Node res;
for (; b; b >>= 1, a = a * a)
if (b & 1)
res = res * a;
return res;
}
Node F(ll n, ll a, ll b, ll c, Node U, Node R) {
if (!n) return Node();
if (b >= c) return QPow(U, b / c) * F(n, a, b % c, c, U, R);
if (a >= c) return F(n, a % c, b, c, U, QPow(U, a / c) * R);
ll m = ((i128) a * n + b) / c;
if (!m) return QPow(R, n);
return QPow(R, (c - b - 1) / a) * U * F(m - 1, c, (c - b - 1) % a, a, R, U) * QPow(R, n - ((i128) c * m - b - 1) / a);
}
ll a, b, c, n;
int main() {
scanf("%lld%lld%lld%lld%d", &a, &c, &b, &n, &m);
Node U, R;
for (int i = 1; i <= m; i++)
for (int j = 1; j <= m; j++)
scanf("%lld", &R.x[i][j]), R.s[i][j] = R.x[i][j];
for (int i = 1; i <= m; i++)
for (int j = 1; j <= m; j++)
scanf("%lld", &U.y[i][j]);
Node ans = F(n, a, b, c, U, R);
for (int i = 1; i <= m; i++)
for (int j = 1; j <= m; j++)
printf("%lld%c", ans.s[i][j], " \n"[j == m]);
return 0;
}
相关推荐
评论
共 2 条评论,欢迎与作者交流。
正在加载评论...