九校联考-长沙市一中NOIP模拟Day1T2 跳房子(jump)
问题描述
跳房子,是一种世界性的儿童游戏,也是中国民间传统的体育游戏之一。
跳房子是在N个格子上进行的,CYJ对游戏进行了改进,该成了跳棋盘,改进后的游戏是在一个N行M列的棋盘上进行,并规定从第一行往上可以走到最后一行,第一列往左可以走到最后一列,反之亦然。每个格子上有一个数字。
在这个棋盘左上角(1,1)放置着一枚棋子。每次棋子会走到右、右上和右下三个方向格子中对应上数字最大一个。即任意时刻棋子都只有一种走法,不存在多个格子同时满足条件。
现在有两种操作:
move k 将棋子前进k步。
change a b e 将第a行第b列格子上的数字修改为e。
请对于每一个move操作输出棋子移动完毕后所处的位置。
输入
第一行包含两个正整数N,M(3<=N,M<=2000),表示棋盘的大小。
接下来N行,每行M个整数,依次表示每个格子中的数字a[i,j](1<= a[i,j]<=109)。
接下来一行包含一个正整数Q(1<=Q<=5000),表示操作次数。
接下来m行,每行一个操作,其中1<=a<=N,1<=b<=M,1<=k,e<=109。
输出
对于每个move操作,输出一行两个正整数x,y,即棋子所处的行号和列号。
输入输出样例
样例输入
4 4
1 2 9 3
3 5 4 8
4 3 2 7
5 8 1 6
4
move 1
move 1
change 1 4 100
move 1
样例输出
4 2
1 3
1 4
数据范围
10%的数据满足:3<= N,M <=50,Q<=5000, k<=10;
20%的数据满足:3<= N,M <=200,Q<=5000,k<=5000;
另有20%的数据满足:3<= N,M <=200,Q<=5000,k<=109;
100%的数据满足:3<= N,M <=2000,Q<=5000,e,k<=109;
记录第一列第i行能走到位置jump[i],询问时先一行一行的走,每次走到jump[i]行第一列,
不足一行时一步一步走,修改时暴力重构jump数组
代码
#include <cstdio>
#include <limits>
#define MAX 2000
int R, C;
int a[MAX][MAX];
int pos_r = 0, pos_c = 0;
int jump[MAX];
int Fix(int x, int X) {
return (x % X + X) % X;
}
int NextRow(int r, int c) {
int next_row = -1;
int max_value = -1;
for (int rr = r - 1; rr <= r + 1; ++rr) {
const int value = a[Fix(rr, R)][Fix(c + 1, C)];
if (value > max_value) {
max_value = value;
next_row = rr;
}
}
return next_row;
}
int RunToFirstColumn(int r, int c) {
do {
r = Fix(NextRow(r, c++), R);
} while (c < C);
return r;
}
struct Interval {
Interval() = default;
Interval(int lo, int hi) : lo(lo), hi(hi) {}
bool IsEmpty() const { return lo > hi; }
int Size() const { return hi - lo + 1; }
bool Contains(int x) const { return lo <= x && x <= hi; }
int lo = std::numeric_limits<int>::max();
int hi = std::numeric_limits<int>::min();
};
void Update(int r, int c) {
const int target_row = RunToFirstColumn(r, c);
Interval rows(r, r);
int cc = c;
while (cc > 0) {
--cc;
Interval new_rows;
for (int rr = rows.lo - 1; rr <= rows.lo + 1; ++rr) {
if (rows.Contains(NextRow(rr, cc))) {
new_rows.lo = rr;
break;
}
}
for (int rr = rows.hi + 1; rr >= rows.hi - 1; --rr) {
if (rows.Contains(NextRow(rr, cc))) {
new_rows.hi = rr;
break;
}
}
if (new_rows.IsEmpty()) return;
rows = new_rows;
}
if (rows.Size() >= R) {
for (int rr = 0; rr < R; ++rr) {
jump[rr] = target_row;
}
} else {
for (int rr = rows.lo; rr <= rows.hi; ++rr) {
jump[Fix(rr, R)] = target_row;
}
}
return;
}
void SuperWalk(int k) {
static int walk_id = 0;
static int last_walk_id[MAX];
static int last_k[MAX];
++walk_id;
if (k >= C - pos_c) {
k -= C - pos_c;
pos_r = RunToFirstColumn(pos_r, pos_c);
pos_c = 0;
}
while (k >= C) {
k -= C;
pos_r = jump[pos_r];
if (last_walk_id[pos_r] == walk_id) {
const int cycle_len = last_k[pos_r] - k;
if (k >= cycle_len) {
const int n_cycles = k / cycle_len;
k -= n_cycles * cycle_len;
}
}
last_walk_id[pos_r] = walk_id;
last_k[pos_r] = k;
}
while (k--) {
pos_r = Fix(NextRow(pos_r, pos_c++), R);
}
}
int main() {
scanf("%d%d", &R, &C);
for (int r = 0; r < R; ++r) {
for (int c = 0; c < C; ++c) {
int k;
scanf("%d", &k);
a[r][c] = k;
}
}
for (int r = 0; r < R; ++r) {
jump[r] = RunToFirstColumn(r, 0);
}
int Q;
scanf("%d", &Q);
for (int qq = 0; qq < Q; ++qq) {
static char command[16];
scanf("%s", command);
if (command[0] == 'm') {
int k;
scanf("%d", &k);
SuperWalk(k);
printf("%d %d\n", pos_r + 1, pos_c + 1);
} else {
int r, c, k;
scanf("%d%d%d", &r, &c, &k); --r; --c;
a[r][c] = k;
for (int rr = r - 1; rr <= r + 1; ++rr) {
Update(Fix(rr, R), Fix(c - 1, C));
}
}
}
return 0;
}