nyoj116 士兵杀敌(二)线段树+树状数组
比士兵杀敌(一)多了一步添加杀敌数。
用add(int k,int a,int b)函数添加杀敌数,k代表当前节点,a是需要添加的士兵,b是添加的数量。判断a在当前节点区间的左子树还是右子树,然后转移状态,直到节点的左右端相等且和要添加的士兵相等,则该节点的sum加上b值并在递归返回的过程中随即更新节点的sum。
#include <stdio.h>
#define maxn 1000000*4+50
struct node{
int l,r,sum;
}t[maxn];
void build(int k,int l,int r){
t[k].l=l;t[k].r=r;
if(t[k].l==t[k].r){
scanf("%d",&t[k].sum);
return;
}
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
t[k].sum=t[k<<1].sum+t[k<<1|1].sum;
}
void add(int k,int a,int b){
if(t[k].l==t[k].r){
t[k].sum+=b;
return;
}
int mid=(t[k].l+t[k].r)>>1;
if(a<=mid) add(k<<1,a,b);
else add(k<<1|1,a,b);
t[k].sum=t[k<<1].sum+t[k<<1|1].sum;
}
int search(int k,int l,int r){
if(t[k].l==l&&t[k].r==r)
return t[k].sum;
int mid=(t[k].l+t[k].r)>>1;
if(mid<l) search(k<<1|1,l,r);
else if(mid>=r) search(k<<1,l,r);
else return search(k<<1,l,mid)+search(k<<1|1,mid+1,r);
}
int main(){
int n,m;
int a,b;
char s[7];
scanf("%d %d",&n,&m);
build(1,1,n);
for(int i=0;i<m;i++){
scanf("%s %d %d",s,&a,&b);
if(s[0]=='Q')
printf("%d\n",search(1,a,b));
else add(1,a,b);
}
}
用树状数组求区间和。
树状数组可以用于求区间和但是不能求区间最大值。但效率较线段树而言高得多。
树状数组有一个有趣的性质:设节点编号为x,那么这个节点管辖的区间为2^k(其中k为x二进制末尾0的个数)个元素。计算2^k则利用机器补码性质x&-x。
#include <bits/stdc++.h>
int a[1000005];
int n;
int lowbit(int x){
return x&(-x);
}
void add(int x,int y){
while(x<=n){
a[x]+=y;
x+=lowbit(x);
}
}
int query(int x){//求a[x]的前缀和
int sum=0;
while(x>0){
sum+=a[x];
x-=lowbit(x);
}
return sum;
}
int main(){
int m,k,a,b;
scanf("%d %d",&n,&m);
char s[7];
for(int i=1;i<=n;i++){
scanf("%d",&k);
add(i,k);
}
for(int i=1;i<=m;i++){
scanf("%s %d %d",s,&a,&b);
if(s[0]=='Q')
printf("%d\n",query(b)-query(a-1));
else add(a,b);
}
}