【JZOJ6278】【NOIP提高组A】跳房子
题目大意
在一个的矩阵上,一个格子跳一步,将会到达中权值最大的格子(保证没有相同权值)。矩阵是循环的,例如从第列往右边跳会到第列,从第行往上跳会到第行,从第行往下跳会到第行。
一个棋子放在,现有两种操作:
- 询问棋子从当前位置跳步后到了哪
- 修改某个格子的权值
你需要回答所有询问
,操作数,权值。
分析
这题最关键的是要发现循环节。
一个点最多跳遍就会跑进一个环里面。对于每个询问,我们用的时间就能把这个环找出来,就不用暴力跳步了。
现在的问题是,找环还是太慢了,是否有更快的方法?
假设棋子从第一列出发,如果能一次跳步,我们找环的时间就是了。所以,若棋子不在第一列,我们就暴力跳到第一列,时间复杂度是。
那么如何维护第一列各行跳步所到达行?DoggyZheng大爷提供了一种神奇的方法:
用一棵下标的线段树,节点开一个大小为的数组,表示第列各行跳步后到达了第列的哪一行
线段树的根代表区间是,你要查询第一列某一行跳步所到达行,直接用根的信息查询就行了。
线段树信息复杂度是,每次修改更改节点数是,修改复杂度就是,由于此题时限,很轻松就过掉了。
总而言之,这题的关键点就两个:
- 想到每次跳步来更快找循环节
- 用线段树维护
emmmm,很有价值的一道题。
Code
#include <cstdio>
#include <cstring>
#define F(i, l, r) for (int i = l; i <= r; ++i)
#define lson rt << 1
#define rson rt << 1 | 1
const int N = 2007;
inline int read()
{
int x = 0, f = 0;
char c = getchar();
for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = 1;
for (; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ '0');
return f ? -x : x;
}
int n, m, q, tx, ty, a[N][N], vis[N];
char opt;
int getnext(int x, int y)
{
int x1 = x > 1 ? x - 1 : n, x2 = x < n ? x + 1 : 1, y1 = y < m ? y + 1 : 1;
if (a[x1][y1] > a[x][y1] && a[x1][y1] > a[x2][y1]) return x1;
if (a[x][y1] > a[x1][y1] && a[x][y1] > a[x2][y1]) return x;
if (a[x2][y1] > a[x][y1] && a[x2][y1] > a[x1][y1]) return x2;
}
int go[N << 2][N];
void ins(int rt, int l, int r, int po, int c)
{
if (l == r) { go[rt][c] = getnext(c, po); return; }
int mid = l + r >> 1;
if (po <= mid) ins(lson, l, mid, po, c);
else ins(rson, mid + 1, r, po, c);
F(i, 1, n) go[rt][i] = go[rson][go[lson][i]];
}
void build(int rt, int l, int r)
{
if (l == r) { F(i, 1, n) go[rt][i] = getnext(i, l); return; }
int mid = l + r >> 1;
build(lson, l, mid), build(rson, mid + 1, r);
F(i, 1, n) go[rt][i] = go[rson][go[lson][i]];
}
int main()
{
freopen("jump.in", "r", stdin);
freopen("jump.out", "w", stdout);
n = read(), m = read();
F(i, 1, n) F(j, 1, m) a[i][j] = read();
build(1, 1, m);
q = read(), tx = 1, ty = 1;
while (q--)
{
scanf(" %c", &opt);
if (opt == 'm')
{
int k = read(), nx, cnt = 0;
while (k > 0 && ty != 1) tx = getnext(tx, ty), ty = ty < m ? ty + 1 : 1, k--;
if (!k) { printf("%d %d\n", tx, ty); continue; }
memset(vis, 0, sizeof(vis));
nx = tx;
while (!vis[nx]) vis[nx] = 1, nx = go[1][nx], cnt += m;
while (k >= m && tx != nx) tx = go[1][tx], k -= m, cnt -= m;
if (tx != nx) while (k > 0) tx = getnext(tx, ty), ty = ty < m ? ty + 1 : 1, k--;
else
{
k %= cnt;
while (k >= m) tx = go[1][tx], k -= m;
while (k > 0) tx = getnext(tx, ty), ty = ty < m ? ty + 1 : 1, k--;
}
printf("%d %d\n", tx, ty);
}
else
{
int x = read(), y = read(), e = read(), x1 = x > 1 ? x - 1 : n, x2 = x < n ? x + 1 : 1, y1 = y > 1 ? y - 1 : m;
a[x][y] = e, ins(1, 1, m, y1, x1), ins(1, 1, m, y1, x), ins(1, 1, m, y1, x2);
}
}
return 0;
}
作者:zjlcnblogs
出处:https://www.cnblogs.com/zjlcnblogs/p/11305879.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· dotnet 源代码生成器分析器入门
· ASP.NET Core 模型验证消息的本地化新姿势
· 对象命名为何需要避免'-er'和'-or'后缀
· “你见过凌晨四点的洛杉矶吗?”--《我们为什么要睡觉》
· 编程神器Trae:当我用上后,才知道自己的创造力被低估了多少
· 开发的设计和重构,为开发效率服务
· 从零开始开发一个 MCP Server!
· Ai满嘴顺口溜,想考研?浪费我几个小时