封面图源 【动漫作业本】:https://space.bilibili.com/488779255
竞赛地址:https://ac.nowcoder.com/acm/contest/82345
J. Final Coordinates
给定一个n*n的矩阵。
初始位置在x,y,初始偏移量为dx,dy,初始时刻为0秒
现在每经过1秒,会发生如下变化
- x变成x+dx(模n),即走了dx个单位
- y变成y+dy(模n),即走了dy个单位
- dx会变成dx+=x+y+t(这里的x,y是上一秒的x,y)
- dy会变成dy+=x+y+t(这里的x,y是上一秒的x,y)
现在要你求t秒后x,y的位置
举例 x,y,dx,dy,t分别为
5 1 2 0 1 2
那么从0秒到2秒的位置变化为
(1, 2) -> (4, 1) -> (3, 1).
数据范围
J. Final Coordinates
这种题,就是变量随着时间的单位不断变化。
一般有几种解题思路。
- 如果数据范围比较小,可以直接线性遍历,我们可以用递推暴力计算。看t的范围,显然这题不行。
- 数据范围大,看题目通过人数又疯狂暴涨的,说不定他们都是猜的!这时候可以试试打表(暴力搜索小数据,观察变量的变化有没有规律。
- 如果上述都不行,那么就只能采取常规的思路了,利用数学的各种加速运算,快速幂、矩阵快速幂,或者是不是可以用数学的一些定理、理论等。
此题显然是有递推公式的。
dx = x * 1 + y \* 1 + dx \* 1 + t \* 1
dy = x * 1 + y \* 1 + dy \* 1 + t \* 1
将上述表达式代入 x = x + dx, y = y + dy,有
x = x * 2 + y \* 1 + dx \* 1 + t \* 1
y = x * 1 + y \* 2 + dy \* 1 + t \* 1
时刻t的变化公式比较简单 t = t + 1
常数1是不变的
1 = 1
利用上述公式,我们就凑成了向量{x, y, dx, dy, t, 1}在时间变化时的递推公式,整理成矩阵如下
2 1 1 1 0 0
1 2 1 1 0 0
1 0 1 0 0 0
0 1 0 1 0 0
1 1 1 1 1 0
0 0 0 0 1 1
解释下上述的参数来源,竖着看就行了。因为矩阵的运算规则,是行乘以列。
矩阵运算单元确定了,我们再确定向量{x, y, dx, dy, t, 1}中每个元素的初始值。
首先x,y,dx,dy是题目给定的,初始值已知。
常数1用1即可。
剩下时刻t的初始值,不是取0,应该取2,因为我们方便计算,我们需要把原始的x,y减一,便于取模运算。那么dx的更新就变为 dx = (x + 1) + (y + 1) + t = x + y + (t + 2),dy同理,所以我们需要把时刻t调整为2开始。
确定好后,答案即为向量{x, y, dx, dy, 2, 1}*mat^t
#define debugging 0
const int maxn = 200010;
const int maxm = 52;
#define ll long long
#define inf 1e18
int mod;
// 矩阵结构体
struct Matrix {
int n, m;
ll A[6][6];
Matrix(int n, int m): n(n), m(m) {
memset(A, 0, sizeof(A));
}
Matrix(int n, int m, ll _A[6][6]): n(n), m(m) {
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
A[i][j] = _A[i][j] % mod;
}
};
// 矩阵乘法
Matrix operator*(Matrix &a, Matrix &b) {
Matrix c(a.n, b.m);
for (int i = 0; i < a.n; i++)
for (int j = 0; j < b.m; j++)
for (int k = 0; k < a.m; k++)
c.A[i][j] = (c.A[i][j] + a.A[i][k] * b.A[k][j] % mod) % mod;
return c;
}
// 矩阵快速幂
Matrix power(Matrix &a, ll b) {
Matrix y(a.n, a.m);
for (int i = 0; i < y.n; i++)
y.A[i][i] = 1;
for (; b; b >>= 1) {
if (b & 1) y = y * a;
a = a * a;
}
return y;
}
int x, y, dx, dy;
ll t;
void solve() {
scanf("%d%d%d%d%d%lld", &mod, &x, &y, &dx, &dy, &t);
--x; --y; // 把范围调整为[0, mod - 1] 方便取模运算
ll a[6][6] = {
{2, 1, 1, 1, 0, 0},
{1, 2, 1, 1, 0, 0},
{1, 0, 1, 0, 0, 0},
{0, 1, 0, 1, 0, 0},
{1, 1, 1, 1, 1, 0},
{0, 0, 0, 0, 1, 1}
};
ll b[6][6] = {
{x, y, dx, dy, 2, 1},
{0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0}
};
Matrix mat(6, 6, a), base(6, 6, b);
Matrix ans = power(mat, t);
ans = base * ans;
// + 1是把范围调整回 [1, mod]
printf("%lld %lld\n", ans.A[0][0] % mod + 1, ans.A[0][1] % mod + 1);
}
int main() {
int t = 1, cas = 1;
// scanf("%d", &t);
while (t--) {
if (debugging) printf("case %d:\n", cas++);
solve();
}
}
/*
Row:
// [x, y, dx, dy, 0, 1]
[x, y, dx, dy, 2, 1]
mat:
2 1 1 1 0 0
1 2 1 1 0 0
1 0 1 0 0 0
0 1 0 1 0 0
1 1 1 1 1 0
0 0 0 0 1 1
*/
往期题解:
2024年国际大学生程序设计竞赛(ACM-ICPC)新疆赛区大赛(AIFBC):https://zhuanlan.zhihu.com/p/698684625
2024年国际大学生程序设计竞赛(ACM-ICPC)新疆赛区大赛(GH):https://zhuanlan.zhihu.com/p/699108788
应该就差DE没有更新了,想看剩余题目更新的小伙伴可以多多点赞。
PS:最近省赛好像比较多,如果友友想看哪些省赛的题解(前提是有可以提交的竞赛网址),欢迎留言评论。