网址https://vjudge.net/problem/POJ-3468
最近学了线段树,搞了一下区间更新。模板题吧。
写一下自己需要记住的点吧。
1.空间要为节点数的4倍,我在网上找了一下,大概意思是这样
建树后可能出现这种情况(图片来自http://scinart.github.io/acm/2014/03/19/acm-segment-tree-space-analysis/)
结果在最下面的一行原本没完全使用却占用了空间,占用的是比这个图刚刚大的完美二叉树空间。
计算一下此图空间:((n-1)*2-1+1)*2-1=4n-5
n-1倒数第二行空间。*2-1,,除去最后一行的总空间,,+1最后一行空间。
2.类型要为long long int 类型,因为最后会超int范围
3.被标记的点已经更新完信息。等待向下更新标记。
4.延迟标记。
思想:对于要更新的区段,将其记录在祖先节点,而不完全将祖先节点记录整个区间
的子孙节点信息更新。当需要使用其下的子孙节点时再向下更新信息。这种方式大大减少了
时间复杂度。
方式:修改与查询时,遇到节点,查看其是否被标记。是的话就向下传递标记,更新子节点信息,并消除父节点标记。
5.建立的图如下。。。(好丑)
6.具体地方在里面备注
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
/******************************************************/
#define LL long long int
#define mem(a,b) memset(a,b,sizeof(a))
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define L rt<<1
#define R rt<<1|1
#define N 100000+9
#define pow(a) a*a
#define INF 0x3f3f3f3f
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
/*********************************************************/
LL n, q;
LL dat[N<<2];
LL sum[N<<2];
LL addmark[N<<2];
void pushup(LL rt){
sum[rt] = sum[L] + sum[R];
}
void pushdown(LL rt, LL len){
if (addmark[rt])
{
addmark[L] += addmark[rt];
addmark[R] += addmark[rt];
sum[L] += addmark[rt]*(len - (len>>1));//这里要用rt,不用L。加更改后的值
sum[R] += addmark[rt]*(len>>1);
addmark[rt] = 0;
}
}
void build(LL l, LL r, LL rt){
addmark[rt] = 0;
if (l == r){
scanf("%lld", &sum[rt]);
return;
}
LL m = (l + r) / 2;
build(lson);
build(rson);
pushup(rt);
}
LL query(LL a,LL b,LL l, LL r, LL rt){
if (a <= l&&r <= b){
return sum[rt];
}
LL m = (l + r) / 2;
LL res = 0;
pushdown(rt, r - l + 1);
if (a <= m)res+=query(a, b, lson);
if (b>m)res+=query(a, b, rson);
return res;
}
void update(LL a, LL b, LL l, LL r, LL rt, LL c){
if (a <= l&&r <= b){
addmark[rt] += c;
sum[rt] += c * (r - l + 1);
return;
}
pushdown(rt, r-l+1); //向下传递标记,更新左右儿子信息
LL m = (l + r) / 2;
if(a<=m)update(a, b, lson, c);
if(b>m)update(a, b, rson, c);
pushup(rt); //向上更新
}
int main(){
while (~scanf("%lld%lld", &n, &q))
{
build(1, n, 1);
while (q--){
char s[2];
scanf("%s", s);
if (s[0] == 'Q'){
LL a, b;
scanf("%lld%lld", &a, &b);
LL res = query(a, b, 1, n, 1);
printf("%lld\n", res);
}
else{
LL a, b, c;
scanf("%lld%lld%lld", &a, &b, &c);
update(a, b, 1, n, 1, c);
}
}
}
}
HDU1689区间更新
更新的时候,从顶节点一路往下推。当遇到标记时,将标记向下传递。因为在某区间段更新是在递归最后实现的,所以之前的传递造成的标记会被更新的标记覆盖。更新结束之后,要实现求和的功能。
求和过程也是一路从上往下找。当遇到有标记的节点时,就返回标记的值*区间长度,即该区间的总和。当搜寻到叶子节点,即l==r时,如果有标记,返回标记,没有则返回1(初始值为1,不曾改变)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
/******************************************************/
#define LL long long int
#define mem(a,b) memset(a,b,sizeof(a))
#define m ((l+r)/2)
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define L rt<<1
#define R rt<<1|1
#define N 100000+1
#define pow(a) a*a
#define INF 0x3f3f3f3f
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
/*********************************************************/
/*1
10
2
1 5 2
5 9 3*/
LL t, n, q;
LL addmark[N<<2];
void pushdown(LL rt){
if (addmark[rt])
{
addmark[L] = addmark[rt];
addmark[R] = addmark[rt];
addmark[rt] = 0;
}
}
void update(LL a, LL b, LL c, LL l, LL r, LL rt){
if (a<=l&&r<=b){
addmark[rt] = c;
return;
}
pushdown(rt);
if (a <= m)update(a, b, c, lson);
if (b > m)update(a, b, c, rson);
}
LL add(LL l, LL r, LL rt){
if (addmark[rt]){
return addmark[rt] * (r - l + 1);
}
if (l == r){
if (addmark[rt])
return addmark[rt];
else
return 1;
}
LL res = 0;
res+=add(lson);
res+=add(rson);
return res;
}
int main(){
cin >> t;
LL cnt = 1;
while (t--){
mem(addmark, 0);
scanf("%lld%lld", &n, &q);
while (q--){
LL a, b, c;
scanf("%lld%lld%lld", &a, &b, &c);
update(a, b, c, 1, n, 1);
}
printf("Case %lld: The total value of the hook is %lld.\n",cnt++, add(1, n, 1));
}
}