洛谷 P1438 无聊的数列 题解 线段树

无聊的数列

题目背景

无聊的 YYB 总喜欢搞出一些正常人无法搞出的东西。有一天,无聊的 YYB 想出了一道无聊的题:无聊的数列。。。

题目描述

维护一个数列 a i a_i ai,支持两种操作:

  • 1 l r K D:给出一个长度等于 r − l + 1 r-l+1 rl+1 的等差数列,首项为 K K K,公差为 D D D,并将它对应加到 [ l , r ] [l,r] [l,r] 范围中的每一个数上。即:令 a l = a l + K , a l + 1 = a l + 1 + K + D … a r = a r + K + ( r − l ) × D a_l=a_l+K,a_{l+1}=a_{l+1}+K+D\ldots a_r=a_r+K+(r-l) \times D al=al+K,al+1=al+1+K+Dar=ar+K+(rl)×D

  • 2 p:询问序列的第 p p p 个数的值 a p a_p ap

输入格式

第一行两个整数 n , m n,m n,m 表示数列长度和操作个数。

第二行 n n n 个整数,第 i i i 个数表示 a i a_i ai

接下来的 m m m 行,每行先输入一个整数 o p t opt opt

o p t = 1 opt=1 opt=1 则再输入四个整数 l   r   K   D l\ r\ K\ D l r K D

o p t = 2 opt=2 opt=2 则再输入一个整数 p p p

输出格式

对于每个询问,一行一个整数表示答案。

样例 #1

样例输入 #1

5 2
1 2 3 4 5
1 2 4 1 2
2 3

样例输出 #1

6

提示

数据规模与约定

对于 100 % 100\% 100% 数据, 0 ≤ n , m ≤ 1 0 5 , − 200 ≤ a i , K , D ≤ 200 , 1 ≤ l ≤ r ≤ n , 1 ≤ p ≤ n 0\le n,m \le 10^5,-200\le a_i,K,D\le 200, 1 \leq l \leq r \leq n, 1 \leq p \leq n 0n,m105,200ai,K,D200,1lrn,1pn

原题

洛谷P1438——传送门

思路

对区间 [ l , r ] [l,r] [l,r] 加上一个首项为 k k k 公差为 d d d 的等差数列,相当于这个区间加上 k − d ∗ l k-d*l kdl 以及各个位置加上下标乘以公差即 i d x ∗ d idx*d idxd。那么我们只需要用线段树维护两个标记 sum1 和 sum2 即可。

代码

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;

int n, m;

#define lc p << 1
#define rc p << 1 | 1

const int maxn = 1e5 + 6;
i64 a[maxn];

struct node
{
    i64 l, r, sum1, sum2;
} tr[maxn * 4];

void build(int p, int l, int r) // p是当前位置,l和r表示区间
{
    if (l == r)
    {
        tr[p] = {l, r, 0, 0};
        return;
    }
    int mid = l + r >> 1;
    build(lc, l, mid);
    build(rc, mid + 1, r);
    tr[p] = {l, r, tr[lc].sum1 + tr[rc].sum1, tr[lc].sum2 + tr[rc].sum2};
}

void update(int p, int l, int r, i64 add1, i64 add2) // 区间加数
{
    if (l <= tr[p].l && r >= tr[p].r)
    {
        tr[p].sum1 += add1;
        tr[p].sum2 += add2;
        return;
    }
    int mid = tr[p].l + tr[p].r >> 1;
    if (l <= mid)
        update(lc, l, r, add1, add2);
    if (r >= mid + 1)
        update(rc, l, r, add1, add2);
}

i64 query(int p, int x, i64 sum1, i64 sum2) // p是当前位置,x表示查询位置
{
    if (tr[p].l == tr[p].r)
    {
        return tr[p].sum1 + sum1 + (tr[p].sum2 + sum2) * (i64)tr[p].l; // 处理等差数列的关键所在
    }
    int mid = tr[p].l + tr[p].r >> 1;
    if (x <= mid)
        return query(lc, x, sum1 + tr[p].sum1, sum2 + tr[p].sum2);
    else
        return query(rc, x, sum1 + tr[p].sum1, sum2 + tr[p].sum2);
}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);

    cin >> n >> m;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
    }
    build(1, 1, n);
    while (m--)
    {
        int op;
        cin >> op;
        if (op == 1)
        {
            int l, r, k, d;
            cin >> l >> r >> k >> d;
            update(1, l, r, k - d * l, d);
        }
        else
        {
            int x;
            cin >> x;
            cout << query(1, x, 0, 0) + a[x] << '\n';
        }
    }

    return 0;
}
  • 34
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值