线段树基础题
操作包括:1.点修改 2.区间修改 3.区间查询
// 线段树基础:数列求和
#include<stdio.h>
#define maxn 10007//数列总个数
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
int sum[maxn<<2],add[maxn<<2];//sum求和,add为惰性标记
int a[maxn],n;//存原数据下标从1开始
//PushUp 更新结点信息,这里是求和 。用左右子结点更新父亲结点
void PushUp(int rt)
{
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
//Build函数建树
void Build(int l,int r,int rt)
{
if(l==r){
sum[rt]=a[l];
return;
}
int m=(l+r)>>1;
//左右递归
Build(ls);
Build(rs);
//更新信息
PushUp(rt);
}
//点修改,使A[L]+=c
void Update1(int L,int c,int l,int r,int rt)
{
if(l==r){//到达叶节点,修改
sum[rt]+=c;
return;
}
int m=(l+r)>>1;
//根据条件判断往左子树调用还是往右子树调用
if(L<=m)
Update1(L,c,ls);
else
Update1(L,c,rs);
PushUp(rt);//子节点更新了,本结点也需要更新
}
//下推标记函数
void PushDown(int rt,int ln,int rn)
{
//ln,rn为左右子树的数量
if(add[rt]){
//下推标记
add[rt<<1]+=add[rt];
add[rt<<1|1]+=add[rt];
//修改子结点的sum是之与对应的add相对应
sum[rt<<1]+=add[rt]*ln;
sum[rt<<1|1]+=add[rt]*rn;
//清除本结点标记
add[rt]=0;
}
}
//区间修改
void Update2(int L,int R,int C,int l,int r,int rt)
{
if(L<=l&&r<=R){//如果本区间完全在操作区间[L,R]以内
sum[rt]+=C*(r-l+1);//更新数字和,向上保持正确
add[rt]+=C;//增加add标记,表示本区间的sum正确
return;
}
int m=(l+r)>>1;
PushDown(rt,m-l+1,r-m);//下推标记
//判断左右子树跟[L,R]有无交集,有交集才递归
if(L<=m)
Update2(L,R,C,ls);
if(R>m)
Update2(L,R,C,rs);
PushUp(rt);//更新本结点信息
}
//区间查询函数
int Query(int L,int R,int l,int r,int rt)
{
if(L<=l&&r<=R)//在区间内,直接返回
return sum[rt];
int m=(l+r)>>1;
//下推标记,否则sum可能不正确
PushDown(rt,m-l+1,r-m);
//累计答案
int ans=0;
if(L<=m)
ans+=Query(L,R,ls);
if(r>m)
ans+=Query(L,R,rs);
return ans;
}
int main()
{
n=100;
for(int i=1;i<=n;i++)
a[i]=i;
//建树
Build(1,n,1);
int ans=Query(1,100,1,n,1);
printf("%d\n",ans);
return 0;
}