题目链接 : poj3468 - A Simple Problem with Integers
思路
线段树的应用,区间更新 和 区间查询,比较困难的就是区间更新了。
面对的问题
线段树之所以能够快速进行区间操作,是因为它的每一个区间分解得到的终止区间都是 O(logn) 级别的,但是如果每一次区间更新都将操作进行到叶子节点,那么操作就会达到 O(nlogn) 级别。
解决的方法
因为很多底层的信息是不会用到的,所以我们在更新时,也像区间查询那样更新到 “终止区间”,将更新信息记录下来即可,这样操作又回到了
O(logn)
级别,只有当我们进行查询是需要用到这个区间的信息的时候,再利用 push_down
操作将当前信息更新到下一层。
具体的操作
对这道题来说每个节点需要维护 和值 和 增量 两个变量。
- 建树,没有什么差别
- 更新,未到达 “终止节点” 时,更新 和值; 到达后,更新 增量
- 查询,未到达 “终止节点” 时,增量 不为 0 进行 push_down 操作,到达后,返回当前 和值。
代码
#include <algorithm>
#include <iostream>
#include <cstdio>
// 方便下面使用
#define lson tree[root].ls
#define rson tree[root].rs
using namespace std;
typedef long long ll;
struct tNode
{
int l, r; // 左右区间
int ls, rs; // 左右子树位置
ll sum, addSum; // 和值、增量
} tree[200100];
int nCount = 1;
int num[100100];
// 建立
void build(int root, int l, int r)
{
tree[root].addSum = 0;
tree[root].l = l;
tree[root].r = r;
if(l==r) tree[root].sum = num[l];
else
{
tree[root].ls = ++nCount;
build(lson, l, (l+r)/2);
tree[root].rs = ++nCount;
build(rson, (l+r)/2+1, r);
tree[root].sum = (tree[lson].sum + tree[rson].sum);
}
}
// 查询
void query(int root, int l, int r, ll &ans)
{
if(tree[root].l==l&&tree[root].r==r)
{
// 返回当前和值
ans += (tree[root].sum + (r-l+1)*tree[root].addSum);
return;
}
else
{
// push_down 操作,将当前信息更新到下一层
if(tree[root].addSum!=0)
{
// 更新当前和值
tree[root].sum += (tree[root].r-tree[root].l+1) * tree[root].addSum;
// 更新下层增量
tree[lson].addSum += tree[root].addSum;
tree[rson].addSum += tree[root].addSum;
// 当前增量置0
tree[root].addSum = 0;
}
// 递归向下求解
if(l>=tree[rson].l)
{
query(tree[root].rs, l, r, ans);
}
else if(r<=tree[lson].r)
{
query(tree[root].ls, l, r, ans);
}
else
{
query(lson, l, tree[lson].r, ans);
query(rson, tree[rson].l, r, ans);
}
}
}
// 更新
void add(int root, int l, int r, ll addnum)
{
if(tree[root].l==l&&tree[root].r==r)
{
// 更新增量
tree[root].addSum += addnum;
return;
}
else
{
// 更新和值
tree[root].sum += ((r-l+1) * addnum);
if(l>=tree[rson].l)
{
add(rson, l, r, addnum);
}
else if(r<=tree[lson].r)
{
add(lson, l, r, addnum);
}
else
{
add(lson, l, tree[lson].r, addnum);
add(rson, tree[rson].l, r, addnum);
}
}
}
int main()
{
int n, q;
int l, r, addnum;
char op[10];
ll ans;
scanf("%d%d", &n, &q);
for(int i=1; i<=n; i++) scanf("%d", num+i);
nCount = 1;
build(1, 1, n);
for(int i=1; i<=q; i++)
{
scanf("%s", op);
if(op[0]=='Q')
{
scanf("%d%d", &l, &r);
ans = 0;
query(1, l, r, ans);
printf("%I64d\n", ans);
}
else
{
scanf("%d%d%d", &l, &r, &addnum);
add(1, l, r, addnum);
}
}
return 0;
}