等差数列加

等差数列加

根号分治

等差数列加 - 题目 - Daimayuan Online Judge

设一个阈值 M

给等差数列的位置上加 k

  1. 若公差 d >= M, 则暴力加,每次操作复杂度最高为 O(nM)
  2. 若公差 d < M, 则可以打标记记录下公差为 d, 第一个加的位置为 y ,给这个等差数列打上一个 +k 的标记

查询

  1. 本来的这个数经过暴力修改后为 a[x]
  2. 再加上打标记的,枚举公差 d, 若满足 xmodd=y, 则可以加,即 +tag[d][xmodd], 一共有 M 个 d 要枚举,复杂度为 O(M)

每次操作复杂度为 O(nMorM), M=n 时取到最小值,总复杂度为 O(qn)

调试技巧:

  1. 可根据分治的两种情况的常数,选择 M, 不一定严格是 n
  2. 对拍的话不用再打个暴力,改几个 M 不同的值,如果不一样就一定错了

其他根号算法

  1. 图染色
  2. 莫队
  3. 整除分块
  4. 和为 n 的一些数(取值为 1~n),最多有 2n 种取值(等差数列前 n 项和可推导出)
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>

using namespace std;
#define endl "\n"

typedef long long ll;
typedef pair<int, int> PII;

const int N = 2e5 + 10, M = 500;
int n, q;
ll tag[M][M];
ll a[N];
int main()
{
	scanf("%d%d", &n, &q);
	while(q--)
	{
		int op;
		scanf("%d", &op);
		if (op == 1)
		{
			int x, y, d;
			scanf("%d%d%d", &x, &y, &d);
			if (x >= M)
			{
				for (int i = y; i <= n; i += x)
					a[i] += d;
			}
			else
				tag[x][y] += d;
		}
		else
		{
			int x;
			scanf("%d", &x);
			ll ans = a[x];
			for (int i = 1; i < M; i++)
				ans += tag[i][x % i];
			cout << ans << endl;
		}
	}
    return 0;
}
posted @   hzy0227  阅读(78)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
· 零经验选手,Compose 一天开发一款小游戏!
点击右上角即可分享
微信分享提示