等差数列加
作者:互联网
等差数列加
根号分治
等差数列加 - 题目 - Daimayuan Online Judge
设一个阈值 \(M\)
给等差数列的位置上加 k
- 若公差 d >= M, 则暴力加,每次操作复杂度最高为 \(O(\frac nM)\)
- 若公差 d < M, 则可以打标记记录下公差为 \(d\), 第一个加的位置为 \(y\) ,给这个等差数列打上一个 +k 的标记
查询
- 本来的这个数经过暴力修改后为 \(a[x]\)
- 再加上打标记的,枚举公差 \(d\), 若满足 \(x\mod d=y\), 则可以加,即 \(+tag[d][x\mod d]\), 一共有 \(M\) 个 d 要枚举,复杂度为 \(O(M)\)
每次操作复杂度为 \(O(\frac nM\;or\;M)\), \(M=\sqrt n\) 时取到最小值,总复杂度为 \(O(q*\sqrt n)\)
调试技巧:
- 可根据分治的两种情况的常数,选择 M, 不一定严格是 \(\sqrt n\)
- 对拍的话不用再打个暴力,改几个 M 不同的值,如果不一样就一定错了
其他根号算法
- 图染色
- 莫队
- 整除分块
- 和为 n 的一些数(取值为 1~n),最多有 \(\sqrt {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;
}
标签:int,复杂度,sqrt,公差,include,等差数列 来源: https://www.cnblogs.com/hzy717zsy/p/16365564.html