一、树状数组
树状数组是一种支持单点修改区间查询的数据结构,这个数组是以二进制的形式存储的,例如7的二进制是111,最右边1代表1,那么c[7]就是从a[7]往前1个数这个区间所有a数组值的和,再例如8的二进制是1000,最右边的1代表8,那么c[8]就代表从a[8]往前8个数(1-8)这个区间所有a数组值的和。
树状数组有两个操作,修改和查询。
修改是往上修改,也就是例如修改a[1],则先修改c[1],再向上修改c[2],然后c[4],直到n为止。
而查询则是向下查询,例如查询前6个数的和,则ans先+=c[6],再+=c[4]。
而无论是修改的从1到2,再到4,再到8,还是查询的从6到4,都需要一个lowbit操作也就是x=x&(~x).
这样就可以在树状数组上进行操作了。
二、线段树
线段树是一个二叉树,每个节点存储的是一段区间的信息,根节点自然就是1-n总区间的信息,
而每个节点的两个子节点则是l到(r-l)/2和(r-l)/2+1到r这两段区间的信息。
线段树有四种操作:区间修改,区间查询,单点修改,单点查询,
单点修改和单点查询直接用类似二分查找的方法来做就行了,
区间查询:
例如一个1-8的线段树,如果要查询3-7,那么先判断3和7分别在左子树还是右子树,如果都有,那么分别修改。
先进入左子树,发现3-4这个节点被3-7完全包含那么直接取这个节点的信息,再看右子树,发现5-6这个节点被完全包含,也直接取这个节点的信息,再找7-8这个节点,发现未完全包含,则再往下查询,又发现7这个节点被完全包含,也直接取这个点的信息。
区间修改:
区间修改是最难的一个操作,他需要用到一个懒惰标记。还用上一个的例子,修改3-7的所有数的值都加1,发现3-4被完全包含,那么将这个节点打上一个值为1的懒惰标记,同样5-6和7两个节点也打上一个值为1的懒惰标记,而当再查询的时候,例如查询的3的值,发现走到3-4这个节点时有一个懒惰标记,那么就将这个标记下传到两个子节点。
总结一下就是,修改时当发现整个区间都包含时就打上一个标记,当查询到有标记的节点上时,就把标记下传。
下面给一个区间修改区间查询的模板
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<ctime>
#include<cstring>
#include<iostream>
#define ls l,m,root<<1
#define rs m+1,r,root<<1|1
#define LL long long
using namespace std;
LL sum[8000001];
LL a1[8000001];
void add(int root)
{
sum[root]=sum[root<<1]+sum[root<<1|1];
}
void build(int l,int r,int root)
{
a1[root]=0;
if(l==r)
{
scanf("%lld",&sum[root]);
return;
}
int m=(l+r)>>1;
build(ls);
build(rs);
add(root);
return;
}
void sign(int root,int len)
{
if(a1[root])
{
a1[root<<1]+=a1[root];
a1[root<<1|1]+=a1[root];
sum[root<<1]+=a1[root]*(len-(len>>1));
sum[root<<1|1]+=a1[root]*(len>>1);
a1[root]=0;
}
}
void change(int L,int R,int c,int l,int r,int root){
if(L<=l&&r<=R)
{
a1[root]+=c;
sum[root]+=(LL)c*(r-l+1);
return;
}
sign(root,r-l+1);
int m=(l+r)>>1;
if(L<=m)
{
change(L,R,c,ls);
}
if(m+1<=R)
{
change(L,R,c,rs);
}
add(root);
}
LL find(int L,int R,int l,int r,int root){
if(L<=l&&r<=R)
{
return sum[root];
}
sign(root,r-l+1);
LL tot=0;
int m=(l+r)>>1;
if(L<=m)
{
tot+=find(L,R,ls);
}
if(m+1<=R)
{
tot+=find(L,R,rs);
}
return tot;
}
int n,m;
int a,b;
char q[4];
int main()
{
scanf("%d%d",&n,&m);
build(1,n,1);
LL c;
for(int i=1;i<=m;i++)
{
scanf("%s",&q[1]);
if(q[1]=='C')
{
scanf("%d%d%lld",&a,&b,&c);
change(a+1,b+1,c,1,n,1);
continue;
}
if(q[1]=='Q')
{
scanf("%d%d",&a,&b);
printf("%lld\n",find(a+1,b+1,1,n,1));
continue;
}
}
return 0;
}