题目大意
对于一个区间有两种操作,给一段区间加上一个等差数列,查询一个点的值,区间修改,单点查询,数据范围适当,显然,可以用线段树。
当进行第一种操作时,\(a[l] + k, a[l + 1] + k + d, a[l + 2] + k + d * 2 ... a[r] + k + d * (r - l)\),很明显一段区间内的每个数据所加的值并不相同,这就违背了线段树在一次给每个数据加上相同值的条件,所以,我们需要思考,如何避免这一问题,给连续一段区间加减,有一种简易手段,差分,因此只要将原数组差分一遍,给差分数组的区间内数据加值即可,求前缀和时,就会累计前面加的\(d\),因此区间修改时,干三件事即可。
update(1, 1, n, l, l, k);
if(l + 1 <= r) update(1, 1, n, l + 1, r, d);
if(r < n) update(1, 1, n, r + 1, r + 1, -(k + (r - l) * d));
这里有一个很重要的点,在后两条更新的语句执行前,一定要判一下边界,以防越界,不判的话,只有\(82pts\)。
代码如下
#include <iostream>
#include <cstdio>
using namespace std;
int n, m;
const int maxn = 1e5 + 5;
int a[maxn];
struct segment_tree
{
int l, r;
long long value;
long long tag;
};
segment_tree tree[maxn * 4];
void pushup(int root)
{
tree[root].value = tree[root << 1].value + tree[(root << 1) + 1].value;
}
void build(int root, int l, int r)
{
tree[root].tag = 0ll;
tree[root].l = l, tree[root].r = r;
if (l == r)
{
tree[root].value = a[l];
return;
}
int mid = (l + r) >> 1;
build(root << 1, l, mid);
build((root << 1) + 1, mid + 1, r);
pushup(root);
}
void pushdown(int root)
{
int l = tree[root].l, r = tree[root].r;
int mid = (l + r) >> 1;
tree[root << 1].tag += tree[root].tag;
tree[(root << 1) + 1].tag += tree[root].tag;
tree[root << 1].value += (mid - l + 1) * tree[root].tag;
tree[(root << 1) + 1].value += (r - mid) * tree[root].tag;
tree[root].tag = 0ll;
}
void update(int root, int l, int r, int L, int R, long long x)
{
if (L <= l && r <= R)
{
tree[root].tag += x;
tree[root].value += (r - l + 1) * x;
return;
}
int mid = (l + r) >> 1;
pushdown(root);
if(L <= mid) update(root << 1, l, mid, L, R, x);
if(R > mid) update((root << 1) + 1, mid + 1, r, L, R, x);
pushup(root);
return;
}
long long query(int root, int l, int r, int L, int R)
{
if(L <= l && r <= R) return tree[root].value;
pushdown(root);
int mid = (l + r) >> 1;
long long res = 0ll;
if(L <= mid) res += query(root << 1, l, mid, L, R);
if(R > mid) res += query((root << 1) + 1, mid + 1, r, L, R);
return res;
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for(int i = n; i >= 1; i--) a[i] = a[i] - a[i - 1];
build(1, 1, n);
while (m--)
{
int opt;
scanf("%d", &opt);
if (opt == 1)
{
int l, r;
long long k, d;
scanf("%d%d%lld%lld", &l, &r, &k, &d);
update(1, 1, n, l, l, k);
if(l + 1 <= r) update(1, 1, n, l + 1, r, d);
if(r < n) update(1, 1, n, r + 1, r + 1, -(k + (r - l) * d));
}
else
{
int p;
scanf("%d", &p);
printf("%lld\n", query(1, 1, n, 1, p));
}
}
return 0;
}
标签:opt,数列,无聊,tree,long,int,区间,root,P1438
From: https://www.cnblogs.com/coder2023/p/18113399