线段树嘛,NOIP的重点,手打模板理解一发(部分借鉴notonlysuccess的线段树,侵删)
题目:线段树模板——洛谷
前言
朴素线段树都是有以下几个操作:
1.query(区间求和)
代码如下
int Query(int L,int R,int l,int r,int root)
{
if(L<=l&&R>=r) return tree[root];
int mid=(l+r)>>1;
int ret=0;
if(L<=mid) ret+=Query(L,R,lson);
if(R>mid) ret+=Query(L,R,rson);
return ret;
}
2.update(单点增减)
void UpDate(int p,int add,int l,int r,int root)
{
if(l==r)
{
tree[root]+=add;
return;
}
int mid=(l+r)>>1;
if(p<=mid) UpDate(p,add,lson);
if(p>mid) UpDate(p,add,rson);
Push_Up(root);
}
当然树的初始化是必要的
1.建树
void Build_Tree(int l,int r,int root)
{
if(l==r)
{
tree[root]=a[l];
return;
}
int mid=(l+r)>>1;
Build_Tree(lson);
Build_Tree(rson);
Push_Up(root);
}
2.权值的更新
void Push_Up(int root)
{
tree[root]=tree[root<<1]+tree[root<<1|1];
}
所以朴素线段树的代码如下
#include<cstdio>
#include<iostream>
#include<cstring>
#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
using namespace std;
int a[5100];
int tree[22000];
char que[10];
int t,n;
void Push_Up(int root)
{
tree[root]=tree[root<<1]+tree[root<<1|1];
}
void Build_Tree(int l,int r,int root)
{
if(l==r)
{
tree[root]=a[l];
return;
}
int mid=(l+r)>>1;
Build_Tree(lson);
Build_Tree(rson);
Push_Up(root);
}
int Query(int L,int R,int l,int r,int root)
{
if(L<=l&&R>=r) return tree[root];
int mid=(l+r)>>1;
int ret=0;
if(L<=mid) ret+=Query(L,R,lson);
if(R>mid) ret+=Query(L,R,rson);
return ret;
}
void UpDate(int p,int add,int l,int r,int root)
{
if(l==r)
{
tree[root]+=add;
return;
}
int mid=(l+r)>>1;
if(p<=mid) UpDate(p,add,lson);
if(p>mid) UpDate(p,add,rson);
Push_Up(root);
}
int main()
{
scanf("%d",&t);
for(int i=1;i<=t;i++)
{
printf("Case %d:\n",i);
scanf("%d",&n);
for(int j=1;j<=n;j++)
{
scanf("%d",&a[j]);
}
Build_Tree(1,n,1);
while(scanf("%s",&que))
{
if(que[0]=='E') break;
else
{
int a,b;
scanf("%d%d",&a,&b);
if(que[0]=='Q')
{
printf("%d\n",Query(a,b,1,n,1));
}
if(que[0]=='A') UpDate(a,b,1,n,1);
if(que[0]=='S') UpDate(a,-b,1,n,1);
}
}
return 0;
}
}
正文
———————————————————————————
大多数线段树不一定是单点加/减权值,会在区间里集体增减。这样会使时间复杂度变得十分……
所以用了【懒惰标记】来优化
(下一段来自notonlysuccess)
成段更新(通常这对初学者来说是一道坎),需要用到延迟标记(或者说懒惰标记),简单来说就是每次更新的时候不要更新到底,用延迟标记使得更新延迟到下次需要更新or询问到的时候
具体操作代码如下(我也不是很懂,今晚听dalao讲QAQ)
void pushdown(int root,int mid)
{
if(col[root])
{
col[root<<1]+=col[root];
col[root<<1|1]+=col[root];
tree[root<<1]+=col[root]*(mid-(mid>>1));
tree[root<<1|1]+=col[root]*(mid>>1);
col[root]=0;
}
}
完整代码如下
#include<iostream>
#include<cstdio>
#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
using namespace std;
long long a[110000],tree[410000],col[410000];
int n,m;
void pushup(int root)
{
tree[root]=tree[root<<1]+tree[root<<1|1];
}
void pushdown(int root,int mid)
{
if(col[root])
{
col[root<<1]+=col[root];
col[root<<1|1]+=col[root];
tree[root<<1]+=col[root]*(mid-(mid>>1));
tree[root<<1|1]+=col[root]*(mid>>1);
col[root]=0;
}
}
void buildtree(int l,int r,int root)
{
col[root]=0;
if(l==r)
{
tree[root]=a[l];
return;
}
int mid=(l+r)>>1;
buildtree(lson);
buildtree(rson);
pushup(root);
}
void update(int L,int R,int add,int l,int r,int root)
{
if(L<=l&&r<=R)
{
col[root]+=add;
tree[root]+=(long long)add*(r-l+1);
return;
}
pushdown(root,r-l+1);
int mid=(l+r)>>1;
if(L<=mid) update(L,R,add,lson);
if(R>mid) update(L,R,add,rson);
pushup(root);
}
long long query(int L,int R,int l,int r,int root)
{
if(L<=l&&R>=r) return tree[root];
pushdown(root,r-l+1);
int mid=(l+r)>>1;
long long ret=0;
if(L<=mid) ret+=query(L,R,lson);
if(R>mid) ret+=query(L,R,rson);
return ret;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
buildtree(1,n,1);
char p;
for(int i=1;i<=m;i++)
{
cin>>p;
int b,c,d;
if(p=='1')
{
cin>>b>>c>>d;
update(b,c,d,1,n,1);
}
if(p=='2')
{
cin>>b>>c;
printf("%lld\n",query(b,c,1,n,1));
}
}
return 0;
}
最后补一发ZYHdalao的线段树模板orz
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const int N=400000;
long long f[N],opt,n,m,k,opy,opx,opr,opl,ans,a[N],tag[N],size[N];
inline long long F()
{
register long long aa,bb;register char ch;
while(ch=getchar(),(ch<'0'||ch>'9')&&ch!='-');ch=='-'?aa=bb=0:(aa=ch-'0',bb=1);
while(ch=getchar(),ch>='0'&&ch<='9')aa=(aa<<3)+(aa<<1)+ch-'0';return bb?aa:-aa;
}
void build(long long x,long long l,long long r)
{
if (l==r)
{
f[x]=a[l];
size[x]=1;
return;
}
int mid=(l+r)>>1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
f[x]=f[x<<1]+f[x<<1|1];
size[x]=size[x<<1]+size[x<<1|1];
}
void down(int x)
{
if (tag[x]!=0)
{
tag[x<<1]+=tag[x];
tag[x<<1|1]+=tag[x];
f[x<<1]+=size[x<<1]*tag[x];
f[x<<1|1]+=size[x<<1|1]*tag[x];
tag[x]=0;
}
}
void calc(long long x,long long l,long long r)//1??,0??
{
if (opl<=l&&opr>=r)
{
if (opt)ans+=f[x];
else tag[x]+=opy,f[x]+=size[x]*opy;
return ;
}
int mid=(l+r)>>1;
down(x);
if (opl<=mid)calc(x<<1,l,mid);
if (opr>mid)calc(x<<1|1,mid+1,r);
f[x]=f[x<<1]+f[x<<1|1];
}
int main()
{
cin>>n>>m;
for (int i=1;i<=n;i++)a[i]=F();
build(1,1,n);
int op;
// for (int i=1;i<=n;i++)
// cout<<f[i]<<' ';
while(m--)
{
op=F();
if (op==1)
{
opt=0;
opl=F(),opr=F(),opy=F();
calc(1,1,n);
}
else if (op==2)
{
opt=1;
ans=0;
opl=F(),opr=F();
calc(1,1,n);
cout<<ans<<endl;
}
}
return 0;
}
To be continue…