小H学物理
小H学物理
题目描述
小 H 今天在学校里学习了物理,物理老师给了小 H 一把能量枪,这把枪能发射波长为 的能量波,小 H 在一个长度为 的设备上进行发射,一束能量值为 能量波在 位置发射,在 处释放一次能量,该处能量值增加 ,能量波衰减能量绝对值减少一。当能量不为 时继续向右传递 距离进行一次能量释放衰减过程,直到能量衰减为 。
物理老师对小 H 发出了两种任务,任务一即发射能量波;任务二计算出设备上给定区间 内的能量总和,请你帮他完成。
输入描述:
第一行输入两个整数 ,分别表示任务总数和设备总长度以及波长。
接下来 行,每行首先输入一个 表示任务类型,
,继续输入 ,表示能量波发射位置,能量波能量;
,继续输入 ,表示每次查询的区间。
输出描述:
对于每次查询,请输出对应区间内的能量总和。
示例1
输入
5 10 3
0 1 4
0 5 2
1 3 6
0 6 -2
1 1 9
输出
5
9
说明
第一次操作后设备上的能量为:
第二次操作后设备上的能量为:
第三次操作后设备上的能量为:
解题思路
注意到每次修改操作只会对满足 的下标 进行,且 是一个固定的很小的数。为此容易想到根据 的结果把所有下标分成 个组。
对于修改操作,首先我们只会考虑第 组内的下标。其中组内操作的区间左端点是第 个元素,右端点是第 (设第 组内的元素个数为 ,有 )。记 是组内第 个元素,因此修改操作就是给 加上一个首相为 ,公差为 的等差数列,即 。
对于查询操作,只需依次遍历 个组,分别求出每个组内满足 的元素(原数组下标)对应的 的和即可。其中第 组的求和区间 要满足,,取 。,取 。另外只有 才合法。
所以现在的问题是如何实现区间加等差数列,查询区间和。一般来说可以用线段树来实现,但从这题题解学到了树状数组的写法,因此记录一下推导过程。
记原数组为 , 的差分数组为 (), 的差分数组为 ()。每次在 的区间 加上首项为 公差为 的等差数列,在对应的差分数组 上等价于 ,在对应的差分数组 上等价于 。这样我们就可以把区间加操作转换成单点修改了。
对 求前缀 的和 等价于
其中 ,因此
所以只需开三个树状数组分别维护 , 和 的前缀和即可。因此 在区间 的和就是 。
在本题中需要对 个组分别开三个树状数组来维护。
AC 代码如下,时间复杂度为 :
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5 + 5, M = 15;
LL tr[3][M][N];
int lowbit(int x) {
return x & -x;
}
void add(LL *tr, int x, LL c) {
for (int i = x; i < N; i += lowbit(i)) {
tr[i] += c;
}
}
LL query(LL *tr, int x) {
LL ret = 0;
for (int i = x; i; i -= lowbit(i)) {
ret += tr[i];
}
return ret;
}
LL pw(int x, int k) {
LL ret = 1;
while (k--) {
ret *= x;
}
return ret;
}
LL query(int k, int r) {
return (r + 1ll) * (r + 2) * query(tr[0][k], r) - (2 * r + 3) * query(tr[1][k], r) + query(tr[2][k], r) >> 1;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m, k;
cin >> k >> n >> m;
while (k--) {
int op, x, y;
cin >> op >> x >> y;
if (!op) {
int k = (x - 1) % m, l = (x - 1) / m + 1, r = min((n - 1 - k) / m + 1, l + abs(y) - 1), d = y > 0 ? -1 : 1;
for (int i = 0; i < 3; i++) {
add(tr[i][k], l, pw(l, i) * y);
add(tr[i][k], l + 1, pw(l + 1, i) * (d - y));
add(tr[i][k], r + 1, pw(r + 1, i) * -(y + (r - l + 1) * d));
add(tr[i][k], r + 2, pw(r + 2, i) * (y + (r - l) * d));
}
}
else {
LL ret = 0;
for (int i = 0; i < m; i++) {
int l = (x - 1 - i + m - 1) / m + 1, r = (y - 1 - i) / m + 1;
if (l <= r) ret += query(i, r) - query(i, l - 1);
}
cout << ret << '\n';
}
}
return 0;
}
参考资料
牛客练习赛131题解:https://ac.nowcoder.com/discuss/1431386
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/18527481
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
2023-11-05 E1. Doremy's Drying Plan (Easy Version)
2023-11-05 D. XOR Construction