线段树(点修改)
线段树是一种数据结构……有了线段树,我们就可以对一个序列进行快速修改并查询某个区间内的最大值、最小值、和等……下面我来说一下线段树是如何实现的
线段树就是一棵二叉树,我们对一棵二叉树上的点依次编号,从根节点开始,我们从1开始编号,然后对于每一层的点从左到右编号,这样我们不难发现有一个很重要的性质,就是对于任意一个非叶子节点,他的左儿子的编号一定是这个节点乘2,右儿子就是这个节点的编号乘2加1。
我对于每一个节点中储存这个节点的所有的儿子节点的最大值、最小值、和,那么我们就一定可以在log(n)的时间内对于要修改的点进行更新,或者对于某个区间进行查询。
线段树一开始肯定感觉有点乱,但最主要的还是看看代码,好好研究一下代码是怎么写的,然后慢慢理解……我是在学了C++半年之后才开始学线段树的……我一开始反正没懂……
代买如下(这就是对于一个序列的点修改和区间查询的代码):
1 #include<iostream> 2 #define maxn 1000000 3 #define INF 2000000000 4 using namespace std; 5 int a[maxn],maxv[3*maxn],minv[3*maxn],sumv[3*maxn]; 6 void build(int L,int R,int o) 7 { 8 if(L==R)maxv[o]=minv[o]=sumv[o]=a[L]; 9 else 10 { 11 int m=L+(R-L)/2,lc=o<<1,rc=lc+1; 12 build(L,m,lc); 13 build(m+1,R,rc); 14 maxv[o]=max(maxv[lc],maxv[rc]); 15 minv[o]=min(minv[lc],minv[rc]); 16 sumv[o]=sumv[lc]+sumv[rc]; 17 } 18 return; 19 } 20 int x,v; 21 void update(int L,int R,int o) 22 { 23 if(L==R)maxv[o]=minv[o]=sumv[o]=v; 24 else 25 { 26 int m=L+(R-L)/2,lc=o<<1,rc=lc+1; 27 if(x<=m)update(L,m,lc); 28 if(x>m)update(m+1,R,rc); 29 maxv[o]=max(maxv[lc],maxv[rc]); 30 minv[o]=min(minv[lc],minv[rc]); 31 sumv[o]=sumv[lc]+sumv[rc]; 32 } 33 } 34 int ql,qr,_max,_min,_sum; 35 void query(int L,int R,int o) 36 { 37 if(ql<=L&&R<=qr) 38 { 39 _max=max(_max,maxv[o]); 40 _min=min(_min,minv[o]); 41 _sum+=sumv[o]; 42 } 43 else 44 { 45 int m=L+(R-L)/2,lc=o<<1,rc=lc+1; 46 if(ql<=m)query(L,m,lc); 47 if(qr>m)query(m+1,R,rc); 48 } 49 return; 50 } 51 int main() 52 { 53 int n,Q; 54 scanf("%d",&n); 55 for(int i=1;i<=n;i++) 56 { 57 scanf("%d",&a[i]); 58 } 59 build(1,n,1); 60 scanf("%d",&Q); 61 while(Q--) 62 { 63 char t; 64 scanf("%s",&t); 65 if(t=='Q') 66 { 67 _sum=0; 68 _min=-1u>>1; 69 _max=1<<31; 70 scanf("%d%d",&ql,&qr); 71 query(1,n,1); 72 printf("MaxNum: %d, MinNum: %d, Sum: %d\n",_max,_min,_sum); 73 } 74 if(t=='C') 75 { 76 scanf("%d%d",&x,&v); 77 update(1,n,1); 78 } 79 } 80 }