NC23054 华华开始学信息学

题目链接

题目

题目描述

因为上次在月月面前丢人了,所以华华决定开始学信息学。十分钟后,他就开始学树状数组了。这是一道树状数组的入门题:

给定一个长度为 N 的序列 A ,所有元素初值为 0 。接下来有 M 次操作或询问:
操作:输入格式:1 D K,将 AD 加上 K
询问:输入格式:2 L R,询问区间和,即 i=LRAi

华华很快就学会了树状数组并通过了这道题。月月也很喜欢树状数组,于是给华华出了一道进阶题:
给定一个长度为 N 的序列 A ,所有元素初值为 0 。接下来有 M 次操作或询问:
操作:输入格式:1 D K,对于所有满足 1iNi0(modD)i ,将 Ai ​加上 K
询问:输入格式:2 L R,询问区间和,即 i=LRAi
华华是个newbie,怎么可能会这样的题?不过你应该会吧。

输入描述

第一行两个正整数 NM 表示序列的长度和操作询问的总次数。
接下来M行每行三个正整数,表示一个操作或询问。

输出描述

对于每个询问,输出一个非负整数表示答案。

示例1

输入

10 6
1 1 1
2 4 6
1 3 2
2 5 7
1 6 10
2 1 10

输出

3
5
26

备注

1N,M1051DN1LRN1K108

题解

知识点:树状数组,根号分治。

显然,这道题的修改并不能转化为可懒标记的区间修改,也没有很好的方法转化为单点修改。

我们可以考虑暴力优化的一种,根号分治。将修改操作的 D 分为两部分,按阈值 B 划分:

  1. DB 时,采用标记法, 用 add 数组表示某个 D 加了多少,复杂度 O(1)
  2. D>B 时,采用暴力修改法,倍增修改树状数组 x0(modD) 的点,复杂度 O(nBlogn)

修改总体复杂度为 O(nBlogn)

同时,查询操作也要随之改变:

  1. DB 部分,暴力累和每个 D 的贡献,即 i=1Baddi(ril1i) ,复杂度 O(B)
  2. D>B 部分,直接查询树状数组即可,复杂度 O(logn)

查询总体复杂度为 O(B+logn)

我们尝试平衡查询和修改的复杂度。假设 B 能使 logn 被忽略,则需要满足 nBlogn=B ,解得 B=nlogn 。因此, B=nlogn 是我们所需要的阈值,其能使总体复杂度为 O(nlogn)

实际上,这道题用理论最优阈值时间不是最优的,用 B=n 快将近一倍,可能由于数据的 D 普遍较小,使得查询代价上升较明显。

这里采用 B=n 阈值。

时间复杂度 O(mnlogn)

空间复杂度 O(n)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
template<class T>
class Fenwick {
int n;
vector<T> node;
public:
Fenwick(int _n = 0) { init(_n); }
void init(int _n) {
n = _n;
node.assign(n + 1, T::e());
}
void update(int x, T val) { for (int i = x;i <= n;i += i & -i) node[i] += val; }
T query(int x) {
T ans = T::e();
for (int i = x;i >= 1;i -= i & -i) ans += node[i];
return ans;
}
T query(int l, int r) { return query(r) - query(l - 1); }
};
struct T {
ll sum;
static T e() { return { 0 }; }
T &operator+=(const T &x) { return sum += x.sum, *this; }
friend T operator-(const T &a, const T &b) { return { a.sum - b.sum }; }
};
ll add[100007];
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n, m;
cin >> n >> m;
Fenwick<T> fw(n);
for (int i = 1;i <= m;i++) {
int op;
cin >> op;
if (op == 1) {
int d, k;
cin >> d >> k;
if (d * d <= n) add[d] += k;
else for (int i = d;i <= n;i += d) fw.update(i, { k });
}
else {
int l, r;
cin >> l >> r;
ll ans = fw.query(l, r).sum;
for (int i = 1;i * i <= n;i++) ans += add[i] * (r / i - (l - 1) / i);
cout << ans << '\n';
}
}
return 0;
}
posted @   空白菌  阅读(124)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
点击右上角即可分享
微信分享提示