CF1207F Remainder Problem

题意

给定一个初始值全部为 00,长度为 5×1055 \times 10^5 的数列,要支持单点加上某个数,以及查询 y,y+x,y+2x,,y+kxy, y + x, y + 2 \cdot x, \cdots, y + k \cdot x 的数列的对应的值的和,直到 y+kx>ny + k \cdot x > n 时停止。

解法

限时 44 秒,考虑分块或线段树。但是分析后可得,线段树难以维护零散的点的和,而普通分块空间会爆。

那么我们还有一个方法:根号分治。这是一种类似分块的方法,他可以使得平均复杂度在 O(nn)O(n \sqrt{n}) 以内,nn 是数列长度。

考虑设 ansi,jans_{i,j} 表示询问输入的是 i,ji, j 时的答案,那么理论空间复杂度是 O(n2)O(n^2) 的,显然不行。我们注意到询问有一个特征,就是当 y,xy, x 都很小的时候暴力会很慢,而当 y,xy, x 很大时暴力可以接受。暴力的每次复杂度大约是 O(nx)O(\frac{n}{x}) 级别的,所以我们只需要记录 ansi,jans_{i,j},其中 i,jni, j \leq \sqrt{n},那么询问的时候,如果块内有答案,直接 O(1)O(1) 输出,不然暴力的理论最坏复杂度还是 O(n)O(\sqrt{n}) 的,单点修改就没什么难度了,只需要从 11n\sqrt{n} 循环即可,也是 O(n)O(\sqrt{n}) 的。

所以总复杂度最坏是 O(nn)O(n \sqrt{n}) 的,最坏的点约 33 秒,稍微卡一下常数,可以通过。

代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;

const int l = 708, N = 5e5 + 5;

int n;
long long ans[l + 5][l + 5], a[N];

int main()
{
	scanf("%d", &n);
	while (n--)
	{
		int opt, x, y;
		scanf("%lld%lld%lld", &opt, &x, &y);
		if (opt == 1ll)
		{
			a[x] += y;
			for (int i = 1; i <= l; i++)
			{
				ans[i][x % i] += y;
			}
		}
		else
		{
			if (x <= l) printf("%lld\n", ans[x][y]);
			else
			{
				long long res = 0;
				for (int i = y; i <= N - 5; i += x) res += a[i];
				printf("%lld\n", res);
			}
		}
	}
	return 0;
}
posted @   HappyBobb  阅读(7)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示