[线段树]
本质为二叉树
用来区间查询,区间修改,
单点查询,单点修改
运用结构体存储。
struct node{
int sum,laze;
}tree[N*4];//四倍空间
//建树
void build_tree(int id,int l,int r){
if(l==r){
tree[id].sum=a[l];
return;
}
int mid=(l+r)/2;
build_tree(id*2,l,mid);
build_tree(id*2+1,mid+1,r);
tree[id].sum=tree[id*2].sum+tree[id*2+1].sum;
}
//区间和查询
int query(int id,int l,int r,int fl,int fr){
if(l==fl&&r==fr){
return tree[id].sum;
}
int mid=(l+r)/2;
if(fr<=mid){
return query(id*2,l,mid,fl,fr);
}else if(fl>mid){
return query(id*2+1,mid+1,r,fl,fr);
}else{
return query(id*2,l,mid,fl,mid)
+query(id*2+1,mid+1,r,mid+1,fr);
}
}
//单点修改
void change_one(int id,int l,int r,int fn,int cs){
if(l==r){
tree[id].sum+=cs;
return ;
}
int mid=(l+r)/2;
if(fn<=mid)change_one(id*2,l,mid,fn,cs);
else change_one(id*2+1,mid+1,r,fn,cs);
tree[id].sum=tree[id*2].sum+tree[id*2+1].sum;
}
//push三件套
void pushup(int id){
tree[id].sum=tree[id*2].sum+t[id*2+1].sum;
}
void pushdown(int id,int l,int r){
if(tree[id].laze){
int mid=(l+r)/2;
put_tag(id*2,l,mid,tree[id].laze);
put_tag(id*2+1,mid+1,r,tree[id].laze);
tree[id].laze=0;
}
}
void put_tag(int id,int l,int r,int val){
tree[id].sum+=1ll*(r-l+1)*val;
tree[id].laze+=val;
}
//区间查询(laze版)
long long query(int id,int l,int r,int fl,int fr){
if(fl==l&&fr==r){
return tree[id].sum;
}
pushdown(id,l,r);
int mid=(l+r)/2;
if(fr<=mid){
return query(id*2,l,mid,fl,fr);
}else if(fl>mid){
return query(id*2+1,mid+1,r,fl,fr);
}else{
return query(id*2,l,mid,fl,mid)
+query(id*2+1,mid+1,r,mid+1,fr);
}
}
//区间加(laze)
void change(int id,int l,int r,int fl,int fr,long long val){
if(fl==l&&fr==r){
put_tag(id,l,r,val);
return ;
}
int mid=(l+r)/2;
if(fr<=mid){
change(id*2,l,mid,fl,fr,val);
}else if(fl>mid){
change(id*2+1,mid+1,r,fl,fr,val);
}else{
change(id*2,l,mid,fl,mid,val);
change(id*2+1,mid+1,r,mid+1,fr,val);
}
pushup(id);
}
1.
accoders线段树 区间最小
思路:
区间查询最小值,把return query(id*2,l,mid,fl,mid)+query(id*2+1,mid+1,r,mid+1,fr);
改为min(query(id*2,l,mid,fl,mid),query(id*2+1,mid+1,r,mid+1,fr));
即可 。
code:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m;
int a[N];
struct node{
int sum,laze;
}tree[N*4];
void build(int id,int l,int r){
if(l==r){
tree[id].sum=a[l];
return ;
}
int mid=(l+r)/2;
build(id*2,l,mid);
build(id*2+1,mid+1,r);
tree[id].sum=min(tree[id*2].sum,tree[id*2+1].sum);
}
int query(int id,int l,int r,int fl,int fr){
if(fl==l&&fr==r){
return tree[id].sum;
}
int mid=(l+r)/2;
if(fr<=mid){
return query(id*2,l,mid,fl,fr);
}else if(fl>mid){
return query(id*2+1,mid+1,r,fl,fr);
}else{
return min(query(id*2,l,mid,fl,mid),query(id*2+1,mid+1,r,mid+1,fr));
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
build(1,1,n);
for(int i=1;i<=m;i++){
int x,y;
cin>>x>>y;
cout<<query(1,1,n,x,y)<<" ";
}
return 0;
}
2.
accoders线段树 带修改的区间最小
思路:
加上单点修改即可,将tree[id].sum=tree[id*2].sum+tree[id*2+1].sum;
改为 tree[id].sum=min(tree[id*2].sum,tree[id*2+1].sum);
即可。
code:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m;
int a[N];
struct node{
int sum,laze;
}tree[N*4];
void build(int id,int l,int r){
if(l==r){
tree[id].sum=a[l];
return ;
}
int mid=(l+r)/2;
build(id*2,l,mid);
build(id*2+1,mid+1,r);
tree[id].sum=min(tree[id*2].sum,tree[id*2+1].sum);
}
int query(int id,int l,int r,int fl,int fr){
if(fl==l&&fr==r){
return tree[id].sum;
}
int mid=(l+r)/2;
if(fr<=mid){
return query(id*2,l,mid,fl,fr);
}else if(fl>mid){
return query(id*2+1,mid+1,r,fl,fr);
}else{
return min(query(id*2,l,mid,fl,mid),query(id*2+1,mid+1,r,mid+1,fr));
}
}
void change(int id,int l,int r,int fn,int val){
if(l==r){
tree[id].sum=val;
return ;
}
int mid=(l+r)/2;
if(fn<=mid){
change(id*2,l,mid,fn,val);
}else{
change(id*2+1,mid+1,r,fn,val);
}
tree[id].sum=min(tree[id*2].sum,tree[id*2+1].sum);
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
build(1,1,n);
for(int i=1;i<=m;i++){
int f;
cin>>f;
if(f==1){
int x,y;
cin>>x>>y;
cout<<query(1,1,n,x,y)<<" ";
}else{
int x,y;
cin>>x>>y;
change(1,1,n,x,y);
}
}
return 0;
}
3.
luogu P3374 【模板】树状数组1
什么,你问为什么树状数组的题要用线段树写?
(别问(划掉))
因为他的区间查询正好是线段树能解决的,也是线段树的弱化版,适合练手。
思路:
考察区间加和区间和,套板子即可。
注:
要开long long。
code:
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int a[N];
struct node{
long long sum,laze;
}tree[N*4];
void build_tree(int id,int l,int r){
if(l==r){
tree[id].sum=a[l];
return;
}
int mid=(l+r)/2;
build_tree(id*2,l,mid);
build_tree(id*2+1,mid+1,r);
tree[id].sum=tree[id*2].sum+tree[id*2+1].sum;
}
int query(int id,int l,int r,int fl,int fr){
if(l==fl&&r==fr){
return tree[id].sum;
}
int mid=(l+r)/2;
if(fr<=mid){
return query(id*2,l,mid,fl,fr);
}else if(fl>mid){
return query(id*2+1,mid+1,r,fl,fr);
}else{
return query(id*2,l,mid,fl,mid)
+query(id*2+1,mid+1,r,mid+1,fr);
}
}
void change_one(int id,int l,int r,int fn,int cs){
if(l==r){
tree[id].sum+=cs;
return ;
}
int mid=(l+r)/2;
if(fn<=mid)change_one(id*2,l,mid,fn,cs);
else change_one(id*2+1,mid+1,r,fn,cs);
tree[id].sum=tree[id*2].sum+tree[id*2+1].sum;
}
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
build_tree(1,1,n);
for(int i=1;i<=m;i++){
int f,x,y;
cin>>f>>x>>y;
if(f==1){
change_one(1,1,n,x,y);
}else{
cout<<query(1,1,n,x,y)<<"\n";
}
}
return 0;
}
4.
luogu P3368 【模板】树状数组 2
来都来了,直接(水(划掉))做完好了。
思路:
区间加和单点查,要用到laze了。
code:
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int a[N];
struct node{
int sum,laze;
}tree[N*4];
void pushup(int id){
tree[id].sum=tree[id*2].sum+tree[id*2+1].sum;
}
void put_tag(int id,int l,int r,int val){
tree[id].sum+=1ll*(r-l+1)*val;
tree[id].laze+=val;
}
void pushdown(int id,int l,int r){
if(tree[id].laze){
int mid=(l+r)/2;
put_tag(id*2,l,mid,tree[id].laze);
put_tag(id*2+1,mid+1,r,tree[id].laze);
tree[id].laze=0;
}
}
void build(int id,int l,int r){
if(l==r){
tree[id].sum=a[l];
return ;
}
int mid=(l+r)/2;
build(id*2,l,mid);
build(id*2+1,mid+1,r);
tree[id].sum=tree[id*2].sum+tree[id*2+1].sum;
}
long long query(int id,int l,int r,int fl,int fr){
if(fl==l&&fr==r){
return tree[id].sum;
}
pushdown(id,l,r);
int mid=(l+r)/2;
if(fr<=mid){
return query(id*2,l,mid,fl,fr);
}else if(fl>mid){
return query(id*2+1,mid+1,r,fl,fr);
}else{
return query(id*2,l,mid,fl,mid)
+query(id*2+1,mid+1,r,mid+1,fr);
}
}
void change(int id,int l,int r,int fl,int fr,long long val){
if(fl==l&&fr==r){
put_tag(id,l,r,val);
return ;
}
int mid=(l+r)/2;
if(fr<=mid){
change(id*2,l,mid,fl,fr,val);
}else if(fl>mid){
change(id*2+1,mid+1,r,fl,fr,val);
}else{
change(id*2,l,mid,fl,mid,val);
change(id*2+1,mid+1,r,mid+1,fr,val);
}
pushup(id);
}
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
build(1,1,n);
for(int i=1;i<=m;i++){
int f;
cin>>f;
if(f==1){
int x,y,k;
cin>>x>>y>>k;
change(1,1,n,x,y,k);
}else{
int x;
cin>>x;
cout<<query(1,1,n,x,x)<<"\n";
}
}
return 0;
}
5.
luogu P3372 【模板】线段树 1
终于是做上线段树的题了。
思路:
区间加和区间查。
code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e5+10;
int a[N];
struct node{
int sum,laze;
}tree[N*4];
void pushup(int id){
tree[id].sum=tree[id*2].sum+tree[id*2+1].sum;
}
void put_tag(int id,int l,int r,int val){
tree[id].sum+=1ll*(r-l+1)*val;
tree[id].laze+=val;
}
void pushdown(int id,int l,int r){
if(tree[id].laze){
int mid=(l+r)/2;
put_tag(id*2,l,mid,tree[id].laze);
put_tag(id*2+1,mid+1,r,tree[id].laze);
tree[id].laze=0;
}
}
void build(int id,int l,int r){
if(l==r){
tree[id].sum=a[l];
return;
}
int mid=(l+r)/2;
build(id*2,l,mid);
build(id*2+1,mid+1,r);
tree[id].sum=tree[id*2].sum+tree[id*2+1].sum;
}
long long query(int id,int l,int r,int fl,int fr){
if(fl==l&&fr==r){
return tree[id].sum;
}
pushdown(id,l,r);
int mid=(l+r)/2;
if(fr<=mid){
return query(id*2,l,mid,fl,fr);
}else if(fl>mid){
return query(id*2+1,mid+1,r,fl,fr);
}else{
return query(id*2,l,mid,fl,mid)
+query(id*2+1,mid+1,r,mid+1,fr);
}
}
void change(int id,int l,int r,int fl,int fr,long long val){
if(fl==l&&fr==r){
put_tag(id,l,r,val);
return ;
}
pushdown(id,l,r);
int mid=(l+r)/2;
if(fr<=mid){
change(id*2,l,mid,fl,fr,val);
}else if(fl>mid){
change(id*2+1,mid+1,r,fl,fr,val);
}else{
change(id*2,l,mid,fl,mid,val);
change(id*2+1,mid+1,r,mid+1,fr,val);
}
pushup(id);
}
signed main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
build(1,1,n);
for(int i=1;i<=m;i++){
int f;
cin>>f;
if(f==1){
int x,y,k;
cin>>x>>y>>k;
change(1,1,n,x,y,k);
}else{
int x,y;
cin>>x>>y;
cout<<query(1,1,n,x,y)<<"\n";
}
}
return 0;
}
标签:fr,int,sum,tree,mid,id,线段
From: https://www.cnblogs.com/123lbh321/p/18682513