封面图源 【动漫作业本】: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

解释下上述的参数来源,竖着看就行了。因为矩阵的运算规则,是行乘以列。

什么,这题不会?这不板子题吗?_快速幂_02

矩阵运算单元确定了,我们再确定向量{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没有更新了,想看剩余题目更新的小伙伴可以多多点赞。

什么,这题不会?这不板子题吗?_简单数论_03

PS:最近省赛好像比较多,如果友友想看哪些省赛的题解(前提是有可以提交的竞赛网址),欢迎留言评论。