关于线段树的详细信息参考以下文章:
本文不介绍线段树的详细信息,只给出线段树的部分模板:
1.区间查询,单点修改
[题目链接:] (https://www.luogu.com.cn/problem/P3374)
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+7;
typedef long long ll;
ll arr[500010],ans;
struct node
{
int l,r; //用于表示该节点包含的赋予范围,范围为[l~r];
ll sum=0; //用于存储该节点所含区域内所有数的和;
};
node tree[500010*4]; //存储线段树;
void build(int p,int l,int r) //递归构建线段树;
{
tree[p].l=l;tree[p].r=r;
if(l==r){ //找到叶子节点,叶子节点存储数组arr内对应的值;
tree[p].sum=arr[l];
return ;
}
int mid=(l+r)/2;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
tree[p].sum=tree[p<<1].sum+tree[p<<1|1].sum; //由二叉树的性质可知右孩子的下标是父节点的两倍,左孩子的下标是父节点的两倍加1;
return ;
}
void add(int p,int x,int k) //单点修改
{
if(tree[p].l==tree[p].r){
tree[p].sum+=k; //递归查找需要修改的叶子节点,更新数值;
return ;
}
int mid=tree[p].l+tree[p].r>>1;
if(x<=mid) add(p<<1,x,k); //如果目标节点在区间左侧,则进入该节点的左孩子,反之进入右孩子中
else add(p<<1|1,x,k);
tree[p].sum=tree[p<<1].sum+tree[p<<1|1].sum; //反向更新包含该节点的所有区间的数值总和
}
ll search(int p, int l,int r) //区间查询
{
if(tree[p].l>=l&&tree[p].r<=r) return tree[p].sum; //如果这个区间被完全包括在目标区间里面,直接返回这个区间的值
if(tree[p].l>r||tree[p].r<l) return 0; //如果这个区间和目标区间毫不相干,返回0
int mid=tree[p].l+tree[p].r>>1;
ll s=0;
if(l<=mid) s+=search(p<<1,l,r); //如果这个区间的左儿子和目标区间又交集,那么搜索左儿子
if(r>mid) s+=search(p<<1|1,l,r); //如果这个区间的右儿子和目标区间又交集,那么搜索右儿子
return s;
}
int main() {
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>arr[i];
build(1,1,n);
int f;
while(m--){
cin>>f;
int a,b;
cin>>a>>b;
if(f==1) add(1,a,b);
else {
ans=search(1,a,b);
cout<<ans<<endl;
}
}
return 0;
}
2.区间修改,单点查询
[题目链接] (https://www.luogu.com.cn/problem/P3368)
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+7;
typedef long long ll;
ll arr[500010],ans;
struct node
{
int l,r;
ll num=0;
};
node tree[500010*4];
void build(int p,int l,int r) //构建线段树,注意与上个模板的区别;
{
tree[p].l=l;tree[p].r=r;
if(l==r){
tree[p].num=arr[l];
return ;
}
int mid=(l+r)/2;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
return ;
}
void add(int p,int l,int r,int k) //区间修改,注意修改的实现;
{
if(tree[p].l>=l&&tree[p].r<=r){
tree[p].num+=k;
return ;
}
int mid=(tree[p].l+tree[p].r)/2;
if(l<=mid) add(p<<1,l,r,k);
if(r>mid) add(p<<1|1,l,r,k);
}
void query(int p, int x) //单点查询
{
ans+=tree[p].num;
if(tree[p].l==tree[p].r) return ;
int mid=(tree[p].l+tree[p].r)/2;
if(x<=mid) query(p<<1,x);
else query(p<<1|1,x);
}
int main() {
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>arr[i];
build(1,1,n);
int f;
while(m--){
cin>>f;
if(f==1){
int x,y,k;
cin>>x>>y>>k;
add(1,x,y,k);
}
else {
int x;
cin>>x;
ans=0;
query(1,x);
cout<<ans<<endl;
}
}
return 0;
}
3.区间修改,区间查询(修改仅适用于加减)
[题目链接] (https://www.luogu.com.cn/problem/P3372)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1000000007;
const int inf=0x3f3f3f;
const int MAX=1000010;
struct node{
int l,r;
ll sum;
int k;
};
node arr[MAX*2];
int pre[MAX];
void init(int i,int a,int b){
int mid=(a+b)/2;
arr[i].l=a;arr[i].r=b;arr[i].k=0;
if(a==b) {
arr[i].sum=pre[a];
return ;
}
init(i*2,a,mid);
init(i*2+1,mid+1,b);
arr[i].sum=arr[i*2].sum+arr[i*2+1].sum;
}
void push_down(int i){
if(arr[i].k!=0){
arr[i*2].k+=arr[i].k;
arr[i*2+1].k+=arr[i].k;
int mid=(arr[i].l+arr[i].r)/2;
arr[i*2].sum+=(mid-arr[i].l+1)*arr[i].k;
arr[i*2+1].sum+=(arr[i].r-mid)*arr[i].k;
arr[i].k=0;
}
}
void fix(int i,int a,int b,int c){
if(arr[i].l>=a&&arr[i].r<=b) {
arr[i].sum+=(arr[i].r-arr[i].l+1)*c;
arr[i].k+=c;
return ;
}
push_down(i);
int mid=(arr[i].l+arr[i].r)/2;
if(mid>=a) fix(i*2,a,b,c);
if(mid<b) fix(i*2+1,a,b,c);
arr[i].sum=arr[i*2].sum+arr[i*2+1].sum;
}
ll seek(int i,int a,int b){
if(arr[i].l>=a&&arr[i].r<=b) return arr[i].sum;
push_down(i);
int mid=(arr[i].l+arr[i].r)/2;
if(mid>=b) return seek(i*2,a,b);
else if(mid<a) return seek(i*2+1,a,b);
else return seek(i*2,a,mid)+seek(i*2+1,mid+1,b);
}
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>pre[i];
init(1,1,n);
while(m--){
int f,a,b,c;
cin>>f;
if(f==1){
cin>>a>>b>>c;
fix(1,a,b,c);
}
else{
cin>>a>>b;
ll ans=seek(1,a,b);
cout<<ans<<endl;
}
}
return 0;
}