「
「
「数据结构
」
」
」第
4
4
4章 线段树
(
(
(前
3
3
3题
)
)
)
目录:
A.求区间和
B.区间查改
C.小白逛公园
大家好 我是个菜鸡 我非常喜欢暴力数据结构 于是我用线段树过了这些题(
A . A. A. 例题 1 1 1 求区间和
分析:
那就树状数组 线段树过了呗 模板
CODE:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<bitset>
#define Ctnue continue
//#pragma GCC optimize(2)
#define reg register
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
const int N=1e5+5;
int n,m;
ll sum[N*4];
void up(int x){
sum[x]=sum[x<<1]+sum[x<<1|1];
}
void add(int x,int l,int r,int p,int val)
{
if(l==r){
sum[x]+=val;
return;
}
int mid=(l+r)>>1;
if(p<=mid) add(x<<1,l,mid,p,val);
if(p>mid) add(x<<1|1,mid+1,r,p,val);
up(x);
}
ll query(int x,int l,int r,int L,int R)
{
if(L<=l&&r<=R) return sum[x];
int mid=(l+r)>>1;
ll ans=0;
if(L<=mid) ans+=query(x<<1,l,mid,L,R);
if(mid<R) ans+=query(x<<1|1,mid+1,r,L,R);
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
while(m--)
{
int kd,x,y;
scanf("%d%d%d",&kd,&x,&y);
if(kd==0) add(1,1,n,x,y);
if(kd==1) printf("%lld\n",query(1,1,n,x,y));
}
return 0;
}
B . B. B. 例题 2 2 2 区间查改
分析:
线段树
在区间
[
a
,
b
]
[a,b]
[a,b]每个数
+
x
+x
+x那就在修改线段树时 把当前区间值
+
x
×
l
e
n
[
a
,
b
]
+x\times len[a,b]
+x×len[a,b]即可
CODE:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<bitset>
#define Ctnue continue
//#pragma GCC optimize(2)
#define reg register
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
const int N=1e6+5;
int n,q,a[N];
ll sum[N*4],lazy[N*4];
void up(int x){
sum[x]=sum[x<<1]+sum[x<<1|1];
}
void down(int x,int l,int r)
{
int mid=(l+r)>>1;
sum[x<<1]+=lazy[x]*(mid-l+1);
lazy[x<<1]+=lazy[x];
sum[x<<1|1]+=lazy[x]*(r-(mid+1)+1);
lazy[x<<1|1]+=lazy[x];
lazy[x]=0;
}
void build(int x,int l,int r)
{
if(l==r){
sum[x]=a[l];
return;
}
int mid=(l+r)>>1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
up(x);
}
void add(int x,int l,int r,int L,int R,int val)
{
if(L<=l&&r<=R){
sum[x]+=1ll*val*(r-l+1);
lazy[x]+=val;
return;
}
down(x,l,r);
int mid=(l+r)>>1;
if(L<=mid) add(x<<1,l,mid,L,R,val);
if(mid<R) add(x<<1|1,mid+1,r,L,R,val);
up(x);
}
ll query(int x,int l,int r,int L,int R)
{
if(L<=l&&r<=R) return sum[x];
down(x,l,r);
int mid=(l+r)>>1;
ll ans=0;
if(L<=mid) ans+=query(x<<1,l,mid,L,R);
if(mid<R) ans+=query(x<<1|1,mid+1,r,L,R);
return ans;
}
int main()
{
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
build(1,1,n);
while(q--)
{
int kd;
scanf("%d",&kd);
if(kd==1){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
add(1,1,n,x,y,z);
}
if(kd==2){
int x,y;
scanf("%d%d",&x,&y);
printf("%lld\n",query(1,1,n,x,y));
}
}
return 0;
}
C . C. C. 例题 3 3 3 小白逛公园
分析:
单点修改 区间最大子段和 那就用
O
(
l
o
g
n
)
O(logn)
O(logn)的线段树
v
a
l
:
[
L
,
R
]
val:[L,R]
val:[L,R]的和
l
s
u
m
:
lsum:
lsum:从左端点
L
L
L开始的最大子段和
r
s
u
m
:
rsum:
rsum:从右端点
R
R
R开始的最大子段和
s
u
m
:
[
L
,
R
]
sum:[L,R]
sum:[L,R]的最大子段和
那这样表示 就可以更新每种信息了
tree[x].val=tree[x<<1].val+tree[x<<1|1].val; //左区间和+右区间和
tree[x].lsum=max(tree[x<<1].lsum,tree[x<<1].val+tree[x<<1|1].lsum); //左区间最大子段和 或 左区间和+右区间左子段和
tree[x].rsum=max(tree[x<<1|1].rsum,tree[x<<1|1].val+tree[x<<1].rsum); //右区间最大子段和 或 右区间和+左区间右子段和
tree[x].sum=max(max(tree[x<<1].sum,tree[x<<1|1].sum),tree[x<<1].rsum+tree[x<<1|1].lsum); //左区间最大子段和 或 右区间最大子段和 或左区间右子段和+右区间左子段和
注意这里:
if(R<=mid) return query(x<<1,l,mid,L,R); //只在左区间 就查询左区间
if(mid<L) return query(x<<1|1,mid+1,r,L,R); //只在右区间 就查询右区间
与普通线段树不同 其他就正常更新了
CODE:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<bitset>
#define Ctnue continue
//#pragma GCC optimize(2)
#define reg register
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
const int N=1e6+6;
struct Seg{
ll lsum,rsum,sum,val;
}tree[4*N];
int n,m,a[N>>1];
void up(int x)
{
tree[x].val=tree[x<<1].val+tree[x<<1|1].val;
tree[x].lsum=max(tree[x<<1].lsum,tree[x<<1].val+tree[x<<1|1].lsum);
tree[x].rsum=max(tree[x<<1|1].rsum,tree[x<<1|1].val+tree[x<<1].rsum);
tree[x].sum=max(max(tree[x<<1].sum,tree[x<<1|1].sum),tree[x<<1].rsum+tree[x<<1|1].lsum);
}
void build(int x,int l,int r)
{
if(l==r)
{
tree[x].lsum=tree[x].rsum=tree[x].sum=a[l];
tree[x].val=a[l];
return;
}
int mid=(l+r)>>1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
up(x);
}
Seg query(int x,int l,int r,int L,int R)
{
if(L<=l&&r<=R) return tree[x];
int mid=(l+r)>>1;
Seg ans,ansl,ansr;
if(R<=mid) return query(x<<1,l,mid,L,R);
if(mid<L) return query(x<<1|1,mid+1,r,L,R);
ansl=query(x<<1,l,mid,L,R);
ansr=query(x<<1|1,mid+1,r,L,R);
ans.val=ansl.val+ansr.val;
ans.lsum=max(ansl.lsum,ansl.val+ansr.lsum);
ans.rsum=max(ansr.rsum,ansr.val+ansl.rsum);
ans.sum=max(max(ansl.sum,ansr.sum),ansl.rsum+ansr.lsum);
return ans;
}
void add(int x,int l,int r,int p,int value)
{
if(l==r){
tree[x].val=value;
tree[x].lsum=tree[x].rsum=tree[x].sum=value;
return;
}
int mid=(l+r)>>1;
if(p<=mid) add(x<<1,l,mid,p,value);
if(p>mid) add(x<<1|1,mid+1,r,p,value);
up(x);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
build(1,1,n);
while(m--)
{
int kd,x,y;
scanf("%d%d%d",&kd,&x,&y);
if(kd==1)
{
if(x>y) swap(x,y);
printf("%lld\n",query(1,1,n,x,y).sum);
}
if(kd==2) add(1,1,n,x,y);
}
return 0;
}