CodeForces:Addition to Segment

292 阅读1分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第17天,点击查看活动详情

题目描述

这是codeforces的线段树专题。

E - Segment Tree, part 1 - Addition to Segment

There is an array of n elements, initially filled with zeros. You need to write a data structure that processes two types of queries:

add to the segment from l to r−1 the number v, find the current value of element i. You may have heard that such requests can be made using the tree of segments with mass change operations (we will talk about it in the next lesson), but this problem can be solved using a regular segment tree.

Input The first line contains two numbers n and m (1≤n,m≤100000), the size of the array and the number of operations. The following lines contain the description of the operations. The description of each operation is as follows:

1 l r v: add the value v to the segment from l to r−1 (0≤l<r≤n, 0≤v≤109). 2 i: find the value of the element with index i (0≤i<n). Output For each operation of the second type, print the corresponding value.

Example input

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

output

3
4
0

问题解析

这题是说,给你一个全是0的数组,进行两种操作,一种是在l~r-1区间里全家上数值v,另一种是找到下标为x的值。

用线段树我们可以在logn的复杂度下做到单点修改和区间和计算。但此时是区间修改,如果还是用一般的线段树,那效率是很低的,不如直接用数组。所以这里我们加入了一个新的概念:懒惰标记。

懒惰标记,简单来说,就是通过延迟对节点信息的更改,从而减少可能不必要的操作次数。每次执行修改时,我们通过打标记的方法表明该节点对应的区间在某一次操作中被更改,但不更新该节点的子节点的信息。实质性的修改则在下一次访问带有标记的节点时才进行。

也就是,当我们要在l到r区间的点都加上x时,我们找到管理这些区间的父节点而不是叶子节点,给这个父节点记录上大小为x的懒惰标记,这样当下次我们计算区间和时,就可以根据 懒惰标记* 该区间的点数 知道这个区间通过1操作增加了多少总和。加上原本父节点存储的总和,就是我们要的区间总和了。 有了懒惰标记,我们就不用真的给区间的点都加上x,通过计算,我们还是可以通过logn的复杂度完成区间的修改和查询。

#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>

#define endl '\n';
typedef long long ll;
typedef pair<int, int> PII;
const int N = 300050;
ll f[4 * N], a[N], n;

void revise(ll k, ll l, ll r, ll x, ll v)
{
    if (l == r )
    {
        f[k] += v;
        return;
    }
    ll m = (l + r) / 2;
    if (x <= m)revise(k + k, l, m, x, v);
    else revise(k + k + 1, m + 1, r, x, v);
    f[k] = f[k + k] + f[k + k + 1];
}

ll calc(ll k, ll l, ll r, ll x, ll y)
{
    if (l == x && r == y)
    {
        return f[k];
    }
    ll m = (l + r) / 2;
    if (y <= m)return calc(k + k, l, m, x, y);
    else
        if (x > m)return calc(k + k + 1, m + 1, r, x, y);
        else return calc(k + k, l, m, x, m) + calc(k + k + 1, m + 1, r, m + 1, y);
}

int main()
{
    int t, st;
    cin >> n >> t;
    while (t--)
    {
        cin >> st;
        if (st == 1)
        {
            ll x, y, v;
            cin >> x >> y >> v;
            revise(1, 1, n, x + 1, v);
            if (y < n) revise(1, 1, n, y + 1 , -v);
        }
        else
        {
            ll x;
            cin >> x;
            cout << calc(1, 1, n, 1, x + 1 ) << endl;
        }
    }
    return 0;
}