Loading

:D 获取中...

Solution - Holes

Link

暴力做是 \(O(nm)\) 的。怎么优化呢?I've no slightest idea😢

结果用到了一个特别神的东西,罗阿姨认为 useless 的东西——分块。想到这个就豁然开朗了!

假设块长为 \(\sqrt{n}\)\(f_i\) 表示从 \(i\) 开始跳在块内的步数,\(to_i\) 表示从 \(i\) 开始跳在块内的最后到达的点。我们分成若干段,如果对于 \(i\),跳完之后还在同一块内就简单转移一下,否则说明跳到其它块里去了,相当于当前块里的结束点,\(to_i\) 即本身,那么 \(f_i = 1, to_i = i\)

询问的话,每次先算当前块内的,然后跳到 \(to_i\) 跳的下一个点,这样就会计算下一个块。如果 \(to_i\) 再跳一步就 \(> n\) 了,直接跳出循环,否则说明还会计算下一个块,直接跳就可以了。\(O(\sqrt{n})\)

更新只更新一段。\(O(\sqrt{n})\)

总的就是 \(O(m \sqrt{n})\)。由于神秘原因块长设为 \(2 \sqrt{n}\) 才可以过。

原因是询问比较多,这样平衡一下。

namespace liuzimingc {
const int N = 2e5 + 5; // i + pos[i] 可能会 RE,开两倍,也可以单独判一下是否 > n
#define endl '\n'

int n, m, p[N], f[N], to[N], block, pos[N], l[N], r[N];

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr); cout.tie(nullptr);
	cin >> n >> m;
	block = (int)2 * sqrt(n);
	for (int i = 1; i <= n; i++) {
		cin >> p[i];
		pos[i] = (i - 1) / block + 1;
	}
	for (int i = 1; i <= n; i++)
		if (!l[pos[i]]) l[pos[i]] = i;
	for (int i = n; i; i--)
		if (!r[pos[i]]) r[pos[i]] = i;
    for (int i = n; i; i--)
		if (pos[i + p[i]] == pos[i]) f[i] = f[i + p[i]] + 1, to[i] = to[i + p[i]]; // 简单转移一下
		else f[i] = 1, to[i] = i;
	while (m--) {
		int op, a;
		cin >> op >> a;
		if (op) {
			int sum = 0;
			while (true) {
				sum += f[a];
				a = to[a];
				if (a + p[a] > n) break;
				a += p[a];
			}
			cout << a << " " << sum << endl;
		}
		else {
			cin >> p[a];
			for (int i = r[pos[a]]; i >= l[pos[a]]; i--)
				if (pos[i + p[i]] == pos[i]) f[i] = f[i + p[i]] + 1, to[i] = to[i + p[i]];
				else f[i] = 1, to[i] = i;
		}
	}
	return 0;
}
} // namespace liuzimingc
posted @ 2024-02-06 09:18  liuzimingc  阅读(17)  评论(2编辑  收藏  举报