小H学物理

小H学物理

题目描述

小 H 今天在学校里学习了物理,物理老师给了小 H 一把能量枪,这把枪能发射波长为 λ 的能量波,小 H 在一个长度为 L 的设备上进行发射,一束能量值为 e 能量波在 x 位置发射,在 x 处释放一次能量,该处能量值增加 e,能量波衰减能量绝对值减少一。当能量不为 0 时继续向右传递 λ 距离进行一次能量释放衰减过程,直到能量衰减为 0

物理老师对小 H 发出了两种任务,任务一即发射能量波;任务二计算出设备上给定区间 [l,r] 内的能量总和,请你帮他完成。

输入描述:

第一行输入两个整数 K,L,λ,分别表示任务总数和设备总长度以及波长。

接下来 K 行,每行首先输入一个 op 表示任务类型,

op=0,继续输入 xi,ei,表示能量波发射位置,能量波能量;

op=1,继续输入 li,ri,表示每次查询的区间。

1K105

1L105

1λ10

1x,l,rL

1|e|L

输出描述:

对于每次查询,请输出对应区间内的能量总和。

示例1

输入

5 10 3
0 1 4
0 5 2
1 3 6
0 6 -2
1 1 9

输出

5
9

说明

第一次操作后设备上的能量为:[4,0,0,3,0,0,2,0,0,1]

第二次操作后设备上的能量为:[4,0,0,3,2,0,2,1,0,1]

第三次操作后设备上的能量为:[4,0,0,3,2,2,2,1,1,1]

 

解题思路

  注意到每次修改操作只会对满足 ix(modλ) 的下标 i 进行,且 λ 是一个固定的很小的数。为此容易想到根据 (i1)modλ 的结果把所有下标分成 λ 个组。

  对于修改操作,首先我们只会考虑第 k=(x1)modλ 组内的下标。其中组内操作的区间左端点是第 l=x1λ+1 个元素,右端点是第 r=min{l+|e|1,n1kλ+1}(设第 k 组内的元素个数为 c,有 (c1)λ+k+1nc=n1kλ+1)。记 pi 是组内第 i 个元素,因此修改操作就是给 apl,apl+1,,apr 加上一个首相为 e,公差为 d=sign(e) 的等差数列,即 apiapi+e+(il)d(lir)

  对于查询操作,只需依次遍历 λ 个组,分别求出每个组内满足 pi[l,r] 的元素(原数组下标)对应的 api 的和即可。其中第 k 组的求和区间 [L,R] 要满足,(L1)λ+k+1lLl1kλ+1,取 L=l1kλ+1(R1)λ+k+1rRr1kλ+1,取 R=r1kλ+1。另外只有 LR 才合法。

  所以现在的问题是如何实现区间加等差数列,查询区间和。一般来说可以用线段树来实现,但从这题题解学到了树状数组的写法,因此记录一下推导过程。

  记原数组为 aa 的差分数组为 bbi=aiai1),b 的差分数组为 cci=bibi1)。每次在 a 的区间 [l,r] 加上首项为 k 公差为 d 的等差数列,在对应的差分数组 b 上等价于 {blbl+kbibi+d(l+1ir)br+1br+1(k+(rl)d),在对应的差分数组 c 上等价于 {clcl+kcl+1cl+1k+dcr+1cr+1d(k+(rl)d)cr+2cr+2+k+(rl)d。这样我们就可以把区间加操作转换成单点修改了。

  对 a 求前缀 [1,r] 的和 f(r) 等价于

f(r)=i=1rai=i=1rj=1ibj=i=1rj=1ik=1jck=i=1rj=1ic1+c2++ck++cj=i=1ric1+(i1)c2++(ij+1)cj++ci=r(1+r)2c1+(r1)(1+r1)2c2++(rj+1)(1+rj+1)2cj+(rr+1)(1+rr+1)2cr

  其中 (rj+1)(1+rj+1)2cj=(rj+1)(1+rj+1)2cj=12((r+1)(r+2)cj(2r+3)jcj+cj2),因此

f(r)=r(1+r)2c1+(r1)(1+r1)2c2++(rj+1)(1+rj+1)2cj+(rr+1)(1+rr+1)2cr=12((r+1)(r+2)i=1rci(2r+3)i=1rici+i=1ri2ci)

  所以只需开三个树状数组分别维护 ciicii2ci 的前缀和即可。因此 a 在区间 [l,r] 的和就是 f(r)f(l1)

  在本题中需要对 λ 个组分别开三个树状数组来维护。

  AC 代码如下,时间复杂度为 O(qlognλ)

#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

posted @   onlyblues  阅读(33)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的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
Web Analytics
点击右上角即可分享
微信分享提示