1 条题解
-
0
题目分析
题目要求计算矩阵求和式 (\sum\limits_{x=1}^LA^xB^{\left\lfloor\frac{Px+R}{Q}\right\rfloor}),其中 (A,B) 是 (N \times N) 矩阵。由于 (L) 可达到 (10^{18}),直接遍历计算不可行,需借助类欧几里得算法结合矩阵快速幂,将求和过程转化为递归分治问题。
解题思路
- 矩阵封装:定义矩阵结构体,实现矩阵加法、乘法及快速幂,支持矩阵运算的基本操作。
- 递归分治(类欧几里得):通过类欧几里得算法的思想,将求和范围逐步缩小。每次递归中,根据 (\frac{Px+R}{Q}) 的取值特性,将问题分解为更小的子问题,并结合矩阵快速幂计算幂次项。
- 状态封装:定义
Node结构体封装矩阵状态(包括 (A,B) 的幂次及累加和),通过重载乘法实现状态的快速合并,减少递归中的重复计算。
代码实现(带注释)
#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]; // 矩阵元素(最大N=20) // 空矩阵构造 Matrix() : n(0), m(0) { memset(a, 0, sizeof(a)); } // 单位矩阵构造(n阶) 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; // 矩阵维度N(全局变量,方便Node使用) // 状态结构体:封装A^x、B^y及累加和s struct Node { Matrix x, y, s; // x=A^a, y=B^b, s=累加和 // 初始化: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; // A的幂次相乘 res.y = l.y * r.y; // B的幂次相乘 res.s = l.s + l.x * r.s * l.y; // 累加和合并:l.s + l.x * r.s * l.y return res; } }; // 状态快速幂:计算a^b(b为指数) Node QPow(Node a, ll b) { Node res; // 初始化为单位状态 for (; b; b >>= 1, a = a * a) if (b & 1) res = res * a; return res; } // 类欧几里得递归函数:计算sum_{x=1}^n A^x B^{floor((a*x + b)/c)} Node F(ll n, ll a, ll b, ll c, Node U, Node R) { if (!n) return Node(); // 边界:n=0时返回空状态 // 情况1:b >= c,拆分B的幂次 if (b >= c) return QPow(U, b / c) * F(n, a, b % c, c, U, R); // 情况2:a >= c,拆分A的幂次 if (a >= c) return F(n, a % c, b, c, U, QPow(U, a / c) * R); ll m = ((i128)a * n + b) / c; // 计算floor((a*n + b)/c) if (!m) return QPow(R, n); // 边界:m=0时,B的幂次为0,直接计算R^n(R对应A^x) // 情况3:a < c且b < c,递归转化为对偶问题 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; // 对应题目中的P, R, Q, L int main() { // 输入:P, Q, R, L, N scanf("%lld%lld%lld%lld%d", &a, &c, &b, &n, &m); Node U, R; // U对应B的幂次状态,R对应A的幂次状态 // 输入矩阵A,初始化R.x = A(R表示A^x) 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]; // R.s初始化为A(对应x=1时的项) // 输入矩阵B,初始化U.y = B(U表示B^y) 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 ", ans.s[i][j]); printf("\n"); } return 0; }代码解释
- 矩阵结构体:封装矩阵的基本运算(加法、乘法),支持单位矩阵初始化,满足矩阵幂次计算需求。
- 状态结构体:通过
Node封装 (A^x)、(B^y) 及累加和,重载乘法实现状态合并,适配递归分治中的子问题组合。 - 类欧几里得递归:通过分解 (\left\lfloor\frac{Px+R}{Q}\right\rfloor) 的取值,将原问题转化为更小的子问题,结合矩阵快速幂高效计算幂次项。
- 主函数:初始化矩阵 (A,B) 对应的状态,调用递归函数求解并输出结果。
- 1
信息
- ID
- 5641
- 时间
- 2000ms
- 内存
- 512MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者