题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1166
原来都是用的别人的模板, 这次自己又重新看了一遍线段树(基础), 重新回顾了一遍, 敲了一遍模板
模板的注释写的蛮详细的, 这里就不讲解线段树了, emmmm
代码
用了懒标记效率会提升很多!!
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
int node[MAXN<<2], lazy[MAXN<<2];//为什么空间要开节点的4倍 请移步至:https://blog.csdn.net/liqiming100/article/details/82319686
int num[MAXN];
//线段树模板(以区间和为例)
//更新数据
inline void PushUp(int root)
{
node[root] = node[root<<1] + node[root<<1|1];
}
//1.建树
inline void BuildTree(int root, int l, int r) //[l,r]当前节点的区间, root 当前节点的编号
{
if(l == r){
//说明当前已经到达叶子节点
node[root] = num[l]; //建树先左后右
return ;
}
int mid = (l + r) >> 1;
//继续递归建树
BuildTree(root<<1, l, mid);//左
BuildTree(root<<1|1, mid + 1, r);//右
//其他操作(例如更新区间和)
PushUp(root);
}
//2.单点修改
inline void Update(int root, int l, int r, int index, int value)
{
// root 当前节点编号 [l,r] 树的区间范围, index 修改的节点编号, value 修改的值
if(l == r){
//到达当前节点
node[root] += value;
return ;
}
int mid = (l + r) >> 1;
if(index <= mid)
Update(root<<1, l, mid, index, value);
else
Update(root<<1|1, mid + 1, r, index, value);
//修改后记得更新
PushUp(root);
}
//3.区间修改
//区间修改为了优化效率,我们引入'懒标记' lazy, 所以我们需要一个向下推(向叶节点更新的函数)
inline void PushDown(int root, int ln, int rn)
{
// root 当前节点编号, ln 左子树节点个数, rn 右子树节点个数
if(lazy[root]){
//为什么是加上而不是直接赋值, 可能存在连续的进行区间修改
lazy[root<<1] += lazy[root];
lazy[root<<1|1] += lazy[root];
//更新结果
node[root<<1] += lazy[root] * ln;
node[root<<1|1] += lazy[root] * rn;
//清除标记
lazy[root] = 0;
}
}
inline void Update(int root, int l, int r, int L, int R, int value)
{
// root 当前节点的编号, [l,r] 线段树区间, [L,R] 需要修改的区间范围 , value 修改的值
if(L <= l && r <= R){
//说明在修改的区间范围
node[root] += (value * (r - l + 1)); //直接修改这个节点的值(得到当前区间正确的值)
lazy[root] += value; //下面的节点值用lazy来修改
return ;
}
int mid = (l + r) >> 1;
//下推lazy
PushDown(root, mid - l + 1, r - mid);
if(L <= mid)
Update(root<<1, l, mid, L, R, value);
if(R > mid)
Update(root<<1|1, mid + 1, r, L, R, value);
//更新
PushUp(root);
}
//4.区间查询
inline int Query(int root, int l, int r, int L, int R)
{
// root 当前节点的编号, [l,r] 线段树区间, [L,R] 需要修改的区间范围
if(L <= l && r <= R){
//当前区间在查询区间内, 直接返回节点值
return node[root];
}
int mid = (l + r) >> 1;
//下推lazy
PushDown(root, mid - l + 1, r - mid);
int ans = 0;
if(L <= mid)
ans += Query(root<<1, l, mid, L, R);
if(R > mid)
ans += Query(root<<1|1, mid + 1, r, L, R);
return ans;
}
int main()
{
ios::sync_with_stdio(false);
int t;
int n;
string s;
int a, b, c;
while(cin >> t){
for(int i = 1; i <= t; i++){
cout << "Case " << i << ":" << endl;
cin >> n;
for(int j = 1; j <= n; j++){
cin >> num[j];
}
BuildTree(1,1,n);
while(cin >> s && s != "End"){
if(s == "Query"){
cin >> a >> b;
cout << Query(1, 1, n, a, b) << endl;
}
else if(s == "Add"){
cin >> a >> b;
Update(1, 1, n, a, b);
}
else if(s == "Sub"){
cin >> a >> b;
Update(1, 1, n, a, -b);
}
}
}
}
return 0;
}