因为加的数是等差数列,等差数列相加依旧是等差数列。而线段树存储的特点又是一个又一个的区间,所以可以在加一个等差数列的同时,用 tag 分别记录当前区间对应的首项和公差。然后套模板即可。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+5;
#define int long long
typedef pair<int, int>PII;
int n, m, k, cnt;
int a[N], tree[4*N];
struct node
{
int la, dd;
}tag[N*4]; //tag标签存两个值,一个是当前节点所对应区间的首项,另一个存的公差
void push_up(int rt)
{
tree[rt] = tree[rt<<1] + tree[rt<<1|1];
}
void push_down(int rt, int l, int r)
{
if(tag[rt].la||tag[rt].dd)//一定要两个值都判断是否为零,存在首项为零,公差不为零的情况。就是因为一开始只判断了首项,调试了四五个小时
{
int mid = (l + r) >> 1;
int d = tag[rt].dd; // 公差
int tt1 = tag[rt].la, c1 = mid - l + 1; //左子树的首项,序列长度
int tt2 = (mid - l + 1) * d + tt1, c2 = r - mid; //右子树的首项,序列长度
//处理左子树
tree[rt<<1] += c1*tt1 + c1*(c1-1)*d/2;
tag[rt<<1].la += tt1, tag[rt<<1].dd += d;
//处理右子树
tree[rt<<1|1] += c2*tt2 + c2*(c2-1)*d/2;
tag[rt<<1|1].la += tt2, tag[rt<<1|1].dd += d;
//记得原节点tag归零
tag[rt].la = 0, tag[rt].dd = 0;
}
}
void build(int rt, int l, int r)
{
if(l==r)
{
tree[rt] = a[r];
return;
}
int mid = (l + r) >> 1;
build(rt<<1, l, mid);
build(rt<<1|1, mid + 1, r);
push_up(rt);
}
//tt0是需要加值操作的整个区间首项,tt是当前区间加值操作的区间首项,cnt - 1是已经有多少数字进行过加值操作
//cnt 指当前区间第一个值将要加的数,相对于其所在的等差数列中是第几个数
void updata(int rt, int l, int r, int start, int end, int tt0, int tt, int d)
{
tt = (cnt - 1) * d + tt0; //当前区间的首项 = 整个区间的首项 + (已经加过值的数字个数 - 1)* 公差
if(start <= l && end >= r) //即 an = a1 + (n - 1)* d;
{
cnt += r - l + 1;
int c = r - l + 1;
tree[rt] += c*tt + c*(c-1)*d/2;
tag[rt].la += tt, tag[rt].dd += d;
return;
}
push_down(rt, l, r);
int mid = (l + r) >> 1;
if(start <= mid) updata(rt<<1, l, mid, start, end, tt0, tt, d);
if(end >= mid + 1) updata(rt<<1|1, mid+1, r, start, end, tt0, tt, d);
push_up(rt);
}
int query(int rt, int l, int r, int start, int end) //纯模板
{
if(start <= l && end >= r)
return tree[rt];
push_down(rt, l, r);
int mid = (l + r) >> 1;
int res = 0;
if(start <= mid)
res += query(rt<<1, l, mid, start, end);
if(end >= mid + 1)
res += query(rt<<1|1, mid+1, r, start, end);
return res;
}
signed main()
{
ios::sync_with_stdio(false), cin.tie(0);
cin >> n >> m;
for(int i = 1; i <= n; i ++) cin >> a[i];
build(1, 1, n);
for(int i = 1; i <= m; i ++)
{
cin >> k;
if(k == 1)
{
int start, end, K, D;
cin >> start >> end >> K >> D;
cnt = 1;
updata(1, 1, n, start, end, K, K, D);
}
else
{
int p;
cin >> p;
int t = query(1, 1, n, p, p);
cout << t << '\n';
}
}
return 0;
}