线段树分析
线段树
线段树是是一种二叉搜索树,即每次都分成两个区间,可用线段树快速的查找某一个节点在若干个线段中出现的次数,时间复杂度O(logn)。空间复杂度2N,因此有时需要离散化进行空间压缩。
线段树一般需要三个函数来使用
1,建树(build函数)
2、更新树的节点或区间(update函数)
3、查询(qurey函数)
理论基础有了,那么该怎么代码该怎么实现呢。
这里最主要的问题就是想办法将每个节点它的左儿子和右儿子更好的找到,那么我们发现存在这么一个现象,如果将【1,10】标记为1,那么就可以发现这个结论 1
2 3
4 5 6 7
8 9 10 11 12 13 14 15
16 17 24 25
这样我们就发现存在这么个情况sum[node]=sum[node<<1]+sum[node<<1|1];
Sum是我们创的一个数组,用来存放区间所代表的值或其他的东西。
接着我们对build函数进行递归处理,直到找到根节点,那么就输入你想给它的值,然后层层递归回来就ok了。
到这为止我们就基本可以完成了线段树的创建。那么更新函数则一般根据不同的需要写的方法也不尽相同。同样也通常是递归描述。
需要注意的是如果是区间更新,一般会用一个懒惰标记来标记那些区间是所更新的区间的真子集。这样如果不用的话就不用递归下去,优化了时间和空间。当要查询的时候再进行下放标记就ok。这些操作都需要一个pushdown函数来辅助。下面会介绍pushdown函数。同样也是根据需要来写的
线段树到这基本上一般的题目都可以解决的。
当然线段树也有一维二维之说,类比树状数组。
讲下lazy数组吧,资料来源
http://hihocoder.com/problemset/problem/1078
这样我们明显的能看到可以说优化了快一半的时间了,实际上在做题的时候,当数据量很大,需要更新的区间很大的时候lazy数组优化效果更好了。
现在讲下分析下lazy数组。就hiho 的这道题
void pushdown(int node,int len)//len传递的是区间长度
{
if(lazy[node])
/*说明要么是又要更新这个区间,而且这个区间并不是全部更新,那么就需要下放了,另一种就是在查询里面调用了,说明也要下放lazy标记。*/
{
lazy[node<<1]=lazy[node<<1|1]=lazy[node];
sum[node<<1]=(len-(len>>1))*lazy[node];
sum[node<<1|1]=(len>>1)*lazy[node];
lazy[node]=0;//标记清除。
}
}
Pushdown函数都已经出来了,那么我们的代码就清晰了。
当你区间更新都已经熟练掌握了,单点更新就更不用说了。
这道题AC代码如下:
#include<iostream>
#include<stdio.h>
using namespace std;
int sum[1000050];
int n;
int lazy[1000050];
void build(int l,int r,int node)
{
if(l==r)
{
scanf("%d",&sum[node]);
return ;
}
int mid=(l+r)>>1;
build(l,mid,node<<1);
build(mid+1,r,node<<1|1);
sum[node]=sum[node<<1]+sum[node<<1|1];
}
void pushdown(int node,int len)
{
if(lazy[node])
{
lazy[node<<1]=lazy[node<<1|1]=lazy[node];
sum[node<<1]=(len-(len>>1))*lazy[node];
sum[node<<1|1]=(len>>1)*lazy[node];
lazy[node]=0;
}
}
void update(int lx,int rx,int newp,int l,int r,int node)
{
if(lx<=l&&r<=rx)
{
sum[node]=(r-l+1)*newp;
lazy[node]=newp;
return ;
}
pushdown(node,r-l+1);
int mid=(r+l)>>1;
if(lx<=mid)
update(lx,rx,newp,l,mid,node<<1);
if(rx>mid)
update(lx,rx,newp,mid+1,r,node<<1|1);
sum[node]=sum[node<<1]+sum[node<<1|1];
}
int question(int lx,int rx,int l,int r,int node)
{
if(lx<=l&&r<=rx)
{
return sum[node];
}
pushdown(node,r-l+1);
int mid=(l+r)>>1;
int cnt=0;
if(lx<=mid)
cnt+=question(lx,rx,l,mid,node<<1);
if(rx>mid)
cnt+=question(lx,rx,mid+1,r,node<<1|1);
return cnt;
}
int main()
{
cin>>n;
build(1,n,1);
int m;
cin>>m;
while(m--)
{
int order,lx,rx,newp;
cin>>order;
if(order==1)
{
cin>>lx>>rx>>newp;
update(lx,rx,newp,1,n,1);
}
else
{
cin>>lx>>rx;
cout<<question(lx,rx,1,n,1)<<endl;
}
}
return 0;
}