记忆是痛苦的根源。——功夫

Solution - Holes

Link

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

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

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

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

更新只更新一段。O(n)

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

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

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 @   liuzimingc  阅读(17)  评论(2编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示