时隔多年又再次遨游在C++的海洋之中。
话不多说先上题⬇
*1080 线段树练习
时间限制: 1 s
空间限制: 128000 KB
题目描述
一行N个方格,开始每个格子里都有一个整数。现在动态地提出一些问题和修改:提问的形式是求某一个特定的子区间[a,b]中所有元素的和;修改的规则是指定某一个格子x,加上或者减去一个特定的值A。现在要求你能对每个提问作出正确的回答。1≤N<100000,,提问和修改的总数m<10000条。
输入文件第一行为一个整数N,接下来是n行n个整数,表示格子中原来的整数。接下一个正整数m,再接下来有m行,表示m个询问,第一个整数表示询问代号,询问代号1表示增加,后面的两个数x和A表示给位置X上的数值增加A,询问代号2表示区间求和,后面两个整数表示a和b,表示要求[a,b]之间的区间和。
输出描述
共m行,每个整数
样例输入
6
4
5
6
2
1
3
4
1 3 5
2 1 4
1 1 9
2 2 6
样例输出
22
22
数据范围及提示 <small style="box-sizing: border-box; font-size: 13px;">Data Size & Hint</small>
1≤N≤100000, m≤10000 。
不出意外的话,大多数人看到这个题目都会想到暴力执法,可仔细看看数据,暴力是O(nm) 的复杂度,100000*10000肯定是超时的,所以我们只好转换思路。
那么就开始今天的主题“线段树”。
顾名思义,它一定是个树的结构,而且是一颗二叉树。
大概是这样的⤵
image.png
每个节点储存的是r,l分别表示该节点存储区间的左右端点,以及ls,rs
分别表示该节点所连接的左右儿子的节点序号,还有s表示此区间的和。
从图中可以看出,节点所存储的区间是逐渐二分下去的。如1号节点是1--8,则他的左右儿子2,3分别储存的是1--4,5--8.依此类推。
执行操作1时从根节点开始判断被修改的点是否在该节点所储存的区间内,如果在,那么就把改节点的s,也就是区间和加上所增加的数值,然后继续判断左右儿子,一直递归下去,便达到了修改数值的目的。
操作2时,也是从根节点开始,如果所给区间包含了节点储存的区间,那么直接返回该节点的s即可,如果二者完全分离直接返回0,如果以上都不满足,则返回左右儿子的返回值之和。有些抽象,需自己动手领悟。。
那么就上代码了
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
struct node{int l,r,ls,rs,s;}tree[400010];
int a[100010],cnt;
int built(int ll,int rr){
int u=cnt++;
tree[u]=(node){ll,rr,-1,-1,0};
if(ll<rr){
int mid=(ll+rr)/2;
tree[u].ls=built(ll,mid);
tree[u].rs=built(mid+1,rr);
tree[u].s=tree[tree[u].ls].s+tree[tree[u].rs].s;
}
else tree[u].s=a[ll];
return u;
}
void add(int v,int o,int p){
if(tree[v].l<=o&&tree[v].r>=o) tree[v].s+=p;
else return;
//if(tree[v].l>p||tree[v].r<p) return;
//if(tree[v].l==tree[v].r)return;
add(tree[v].ls,o,p);
add(tree[v].rs,o,p);
}
int query(int v,int l1,int r1){
if(tree[v].l>=l1&&tree[v].r<=r1) return tree[v].s;
if(tree[v].r<l1||tree[v].l>r1) return 0;
else return query(tree[v].ls,l1,r1)+query(tree[v].rs,l1,r1);
}
int main(){
int n,m,t,x,y;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
int root=built(1,n);
cin>>m;
for(int i=1;i<=m;i++){
cin>>t>>x>>y;
if(t==1) add(root,x,y);
else cout<<query(root,x,y)<<endl;
}
return 0;
}
谢谢,请留下您的👍!!
网友评论