这里写自定义目录标题
线段树
线段树是一种二叉树结构
一个问题,只要能化成对一些“连续点”的修改和统计问题,基本就可以用线段树来解决了
总原理:
将[1,n]分解成若干特定的子区间(数量不超过4*n)
用线段树对“编号连续”的一些点,进行修改或者统计操作,修改和统计的复杂度都是O(log2(n))(使用线段树可以快速查找某一节点在若干条线段中出现的次数)
用线段树统计的东西,必须符合区间加法,(也就是说,如果已知左右两子树的全部信息,比如要能够推出父节点);否则,不可能通过分成的子区间来得到[L,R]的统计结果。
父亲为I。左儿子是2i,右儿子是2i+1;
以hdu1754为例
1. 建树(上->下)
void buildtree(int i,int left,int right){
node[i].leftt=left;
node[i].rightt=right;
node[i].value=0;
if(left==right){
index[left]=i;
return;
}
buildtree(i*2,left,(int)(floor(left+right)/2.0));
buildtree(i*2+1,(int)(floor(left+right)/2.0)+1,right);
}
//index是记录该节点信息的下标
2. 更新(下->上)
void updatetree(int ri){
if(ri==1) return;
int fi=ri/2;
int a=node[fi<<1].value;
int b=node[(fi<<1)+1].value;
node[fi].value=max(a,b);
updatetree(ri/2);
}
3.查询(上->下)
void query(int i,int left,int right){
if(node[i].leftt==left&&node[i].rightt==right){
MAX=max(MAX,node[i].value);
return;
}
i=i<<1;
if(left<=node[i].rightt){
if(right<=node[i].rightt)
query(i,left,right);
else query(i,left,node[i].rightt);
}
i++;
if(right>=node[i].leftt){
if(left>=node[i].leftt) query(i,left,right);
else query(i,node[i].leftt,right);
}
}
整体代码
int main(){
int n,m,q;
//ios::sync_with_stdio(false);
while(cin>>n>>m){
buildtree(1,1,n);
for(int i=1;i<=n;i++){
cin>>q;
node[index[i]].value=q;
updatetree(index[i]);
}
char op;
int a,b;
while(m--){
cin>>op>>a>>b;
if(op=='Q'){
MAX=0;
query(1,a,b);
cout<<MAX<<endl;
}
else{
node[index[a]].value=b;
updatetree(index[a]);
}
}
}
return 0;
}
树状数组
什么是树状数组
黑色数组代表原来的数组(下面用A[i]代替),红色结构代表我们的树状数组(下面用C[i]代替),发现没有,每个位置只有一个方框,令每个位置存的就是子节点的值的和,则有
C[1] = A[1];
C[2] = A[1] + A[2];
C[3] = A[3];
C[4] = A[1] + A[2] + A[3] + A[4];
C[5] = A[5];
C[6] = A[5] + A[6];
C[7] = A[7];
C[8] = A[1] + A[2] + A[3] + A[4] + A[5] + A[6] + A[7] + A[8];
可以发现,这颗树是有规律的
C[i] = A[i - 2k+1] + A[i - 2k+2] + … + A[i]; //k为i的二进制中从最低位到高位连续零的长度
例如i = 8(1000)时候,k = 3,可自行验证。
这个怎么实现求和呢,比如我们要找前7项和,那么应该是SUM = C[7] + C[6] + C[4];
而根据上面的式子,容易的出SUMi = C[i] + C[i-2k1] + C[(i - 2k1) - 2k2] + …;
其实树状数组就是一个二进制上面的应用。
即 2^k=i&(-i)
而且这个有一个专门的称呼,叫做lowbit,即取2^k
建立树状数组
int lowbit(int x){
return x&(-x);
}
void update(int i,int k){
while(i<=n){
c[i]+=k;
i+=lowbit(i);
}
}
int getsum(int i){
int res=0;
while(i>0){
res+=c[i];
i-=lowbit(i);
}
return res;
}