线段树是用来对一堆数据处理的树结构,它的核心思想是二分(一般都用递归实现)。

树里需要存的是区间的左右,还有就是看题目需要了

二叉树特性:1:左子树编号是父树的2倍,右子树是父树的2倍加一 。
              2:左子树l是父树的l,左子树的r是父树的(l+r)/2;右子树的l是父树的(l+r)/2 +1,右子树的r是父树的r

如图:

图是偷来的,推荐那个大佬的博客吧:http://www.cnblogs.com/TheRoadToTheGold/p/6254255.html

先看下面这个例题: 

现在给定一组有序数列(数据量一般都超级大),然后问你某个区间的值(是不是觉得用前缀和就可以写?但是他还有另外一个条件),而且在询问你的过程中,他还可以对序列中的数进行更改,再接着询问你某个区间的和。(现在应该不能用前缀和写了吧,毕竟数据量一大,它改了某个点的值,那你就要改与这个点有关的和全部改变)。这时候,你就可以用线段树来写,它是将区间二分,是一个满二叉树。

看代码吧。

 1 #include<iostream>
 2 #include<cstdio>
 3 #define Maxn 50010
 4 using namespace std;
 5 int kp;
 6 //:树
 7 struct Tree{
 8     int l;//(l,r)
 9     int r;
10     int sum;//(l,r)上的和
11 }tree[Maxn];
12 //1:建树
13 //:树特性:1:左子树编号是父树的2倍,右子树是父树的2倍加一 。
14 //2:左子树l是父树的l,左子树的r是父树的(l+r)/2;右子树的l是父树的(l+r)/2 +1,右子树的r是父树的r
15 void Btree(int k,int l,int r)//k:节点编号,l:左,r:右
16 {
17     tree[k].l=l,tree[k].r=r;
18     if(l==r){
19         scanf("%d",&kp);
20         tree[k].sum=kp;
21         return ;
22     }
23     int mid=(l+r)/2;
24     Btree(k*2,l,mid);
25     Btree(k*2+1,mid+1,r);
26     tree[k].sum=tree[k*2].sum+tree[k*2+1].sum;
27 }
28 //查询
29 int ans=0;
30 void query(int k,int start,int ed){//要查询的区间 (start,ed)
31
32     if(start<=tree[k].l&&ed>=tree[k].r){
33         ans+=tree[k].sum;
34         return ;
35     }
36
37     //考虑左走还是右走,还是左右都走
38     int mid=(tree[k].l+tree[k].r)/2;
39     if(ed<=mid){//左
40         query(k*2,start,ed);
41
42     }else if(start>=mid+1){//右
43         query(k*2+1,start,ed);
44
45     }else{//左加右
46         query(k*2,start,ed);
47         query(k*2+1,start,ed);
48
49     }
50     return ;
51 }
52
53 //更改
54 void change(int k,int aim,int val){//aim更改的元素下标 ,val是把aim元素更改为val
55
56     if(tree[k].l==tree[k].r&&aim==tree[k].l){
57         tree[k].sum=val;
58         return ;
59     }
60     int mid=(tree[k].r+tree[k].l)/2;
61
62     if(aim>=tree[k].l&&aim<=mid){//l,r要一直逼近aim==tree[k].l,aim==tree[k].r
63         change(k*2,aim,val);
64
65     }else{
66         change(k*2+1,aim,val);
67     }
68
69     tree[k].sum=tree[k*2].sum+tree[k*2+1].sum;//回溯时改变值
70 }
71 int main(){
72     int n,k=1;
73     cin>>n;
74     Btree(1,1,n);//建立一个根节点区间是【1,n】的树
75     //**********演示*****************
76     for(int i=1;i<=7;i++){
77         cout<<"   ("<<tree[i].l<<" , "<<tree[i].r<<")   "<<tree[i].sum<<endl;
78     }
79     query(1,1,3);//询问
80     cout<<ans<<endl;
81     ans=0;
82     change(1,1,100);//改变一个值
83     for(int i=1;i<=7;i++){
84         cout<<"   ("<<tree[i].l<<" , "<<tree[i].r<<")   "<<tree[i].sum<<endl;
85     }
86     query(1,1,3);//再次询问
87     cout<<ans<<endl;
88     return 0;
89 }

View Code

输出:

下面有两道入门题。

poj3246

Balanced Lineup
Time Limit: 5000MS   Memory Limit: 65536K
Total Submissions:68003   Accepted: 31585
Case Time Limit: 2000MS

Description

For the daily milking, Farmer John's N cows (1 ≤ N ≤ 50,000) always line up in the same order. One day Farmer John decides to organize a game of Ultimate Frisbee with some of the cows. To keep things simple, he will take a contiguous range of cows from the milking lineup to play the game. However, for all the cows to have fun they should not differ too much in height.

Farmer John has made a list of Q (1 ≤ Q ≤ 200,000) potential groups of cows and their heights (1 ≤ height ≤ 1,000,000). For each group, he wants your help to determine the difference in height between the shortest and the tallest cow in the group.

Input

Line 1: Two space-separated integers, N and Q
Lines 2..N+1: Line i+1 contains a single integer that is the height of cow i 
Lines N+2..N+Q+1: Two integers A and B (1 ≤ A ≤ B ≤ N), representing the range of cows from A to B inclusive.

Output

Lines 1..Q: Each line contains a single integer that is a response to a reply and indicates the difference in height between the tallest and shortest cow in the range.

Sample Input

6 3
1
7
3
4
2
5
1 5
4 6
2 2

Sample Output

6
3
0

Source

USACO 2007 January Silver

思路:它只需将前面存和改成存最大最小值即可

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #define Maxn 50010
 4 using namespace std;
 5 int kp;
 6 //:树
 7 struct Tree{
 8     int l;//(l,r)
 9     int r;
10     int sum;//(l,r)上的和
11     int minn,maxx;
12 }tree[Maxn*4];//因为是树,所以它是比题目给的点的数据最大还要大,开4倍就一定够
13 //1:建树
14 //:树特性:1:左子树编号是父树的2倍,右子树是父树的2倍加一 。
15 //2:左子树l是父树的l,左子树的r是父树的(l+r)/2;右子树的l是父树的(l+r)/2 +1,右子树的r是父树的r
16 void Btree(int k,int l,int r)//k:节点编号,l:左,r:右
17 {
18     tree[k].l=l,tree[k].r=r;
19     if(l==r){
20         scanf("%d",&kp);
21         tree[k].maxx=tree[k].minn=kp;
22         return ;
23     }
24     int mid=(l+r)/2;
25     Btree(k*2,l,mid);
26     Btree(k*2+1,mid+1,r);
27     tree[k].maxx=max(tree[k*2].maxx,tree[k*2+1].maxx);
28     tree[k].minn=min(tree[k*2].minn,tree[k*2+1].minn);
29 }
30 //查询
31 int Max,Min;
32 void query(int k,int start,int ed){//要查询的区间 (start,ed)
33
34     if(start<=tree[k].l&&ed>=tree[k].r){
35         Max=max(tree[k].maxx,Max);
36         Min=min(tree[k].minn,Min);
37         return ;
38     }
39
40     //考虑左走还是右走,还是左右都走
41     int mid=(tree[k].l+tree[k].r)/2;
42     if(ed<=mid){//左
43         query(k*2,start,ed);
44
45     }else if(start>=mid+1){//右
46         query(k*2+1,start,ed);
47
48     }else{//左加右
49         query(k*2,start,ed);
50         query(k*2+1,start,ed);
51
52     }
53     return ;
54 }
55
56 //更改
57 void change(int k,int aim,int val){//aim更改的元素下标 ,val是把aim元素更改为val
58
59     if(tree[k].l==tree[k].r&&aim==tree[k].l){
60         tree[k].sum=val;
61         return ;
62     }
63     int mid=(tree[k].l+tree[k].r)/2;
64     if(aim>=tree[k].l&&aim<=mid){//l,r要一直逼近aim==l,aim==r
65         change(k*2,aim,val);
66
67     }else{
68         change(k*2+1,aim,val);
69
70     }
71
72     tree[k].maxx=max(tree[k*2].maxx,tree[k*2+1].maxx);
73     tree[k].minn=min(tree[k*2].minn,tree[k*2+1].minn);
74 }
75 int main(){
76     int n,q;
77     int l,r;
78     cin>>n>>q;
79     Btree(1,1,n);
80     while(q--){
81         scanf("%d%d",&l,&r);
82         Max=-1,Min=0x3f3f3f3f;
83         query(1,l,r);
84         printf("%d\n",Max-Min);
85     }
86     return 0;
87 }

View Code

poj2777

Count Color
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions:53502   Accepted: 16107

Description

Chosen Problem Solving and Program design as an optional course, you are required to solve all kinds of problems. Here, we get a new problem.

There is a very long board with length L centimeter, L is a positive integer, so we can evenly divide the board into L segments, and they are labeled by 1, 2, ... L from left to right, each is 1 centimeter long. Now we have to color the board - one segment with only one color. We can do following two operations on the board:

1. "C A B C" Color the board from segment A to segment B with color C. 
2. "P A B" Output the number of different colors painted between segment A and segment B (including).

In our daily life, we have very few words to describe a color (red, green, blue, yellow…), so you may assume that the total number of different colors T is very small. To make it simple, we express the names of colors as color 1, color 2, ... color T. At the beginning, the board was painted in color 1. Now the rest of problem is left to your.

Input

First line of input contains L (1 <= L <= 100000), T (1 <= T <= 30) and O (1 <= O <= 100000). Here O denotes the number of operations. Following O lines, each contains "C A B C" or "P A B" (here A, B, C are integers, and A may be larger than B) as an operation defined previously.

Output

Ouput results of the output operation in order, each line contains a number.

Sample Input

2 2 4
C 1 1 2
P 1 2
C 2 2 2
P 1 2

Sample Output

2
1

Source

POJ Monthly--2006.03.26,dodo
题意:给你一块长木板,将木板分成编号1~L的L段,然后C是代表将编号为A和B之间的木板颜色改为C,P代表它询问你编号为A和B之间的木板颜色一共有几种(!!!注意输入的并不能保证是前面的那个数小于后面那个,即A不一定小于B,所以区间是[min(A,B),max(A,B)])。
思路:这题因为颜色只有30种,所以可以用二进制存颜色,即用二进制中的1的位置代表是什么颜色,这样存下颜色在算有几种颜色时可用位运算“ | ”运算,因为它只要相应的二进制位有1,那他相应位就为1,这样就不用会算到重复的颜色。至于线段树中存的内容就是color(颜色)(二进制,即颜色种类的数目)和l,r。然后就是按题目要求改变某区间的颜色,这里有一个十分重要的知识,就是“懒标记”。这在我上面推荐的博客上也有,那位大佬讲的十分好(http://www.cnblogs.com/TheRoadToTheGold/p/6254255.html)。
剩下的看代码吧:

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<string.h>
  4 #define Maxn 100010
  5 #define LL long long
  6 using namespace std;
  7 //线段树懒标记要改的地方都有!!!
  8 struct Tree{
  9     int l,r;
 10     int k;
 11     int f;
 12     LL color;
 13 }tree[Maxn*5];
 14
 15 void BTree(int k,int l,int r){
 16     tree[k].l=l,tree[k].r=r;
 17     if(l==r){
 18         tree[k].color=2;//因为是二进制存颜色,输入是1~T,所以这写1,2都没错
 19         return ;
 20     }
 21
 22     int mid=(l+r)/2;
 23     BTree(k*2,l,mid);
 24     BTree(k*2+1,mid+1,r);
 25
 26     tree[k].color=(tree[k*2].color|tree[k*2+1].color);// 更新父节点颜色数
 27
 28 }
 29
 30 void push_down(int k){//懒标记的下传操作
 31     if(tree[k].l==tree[k].r) return ;
 32     tree[k*2].f=tree[k].f;//要将父节点保留的懒标记传给子节点
 33     tree[k*2+1].f=tree[k].f;
 34     tree[k*2].color=1<<tree[k].f;//更新节点颜色
 35     tree[k*2+1].color=1<<tree[k].f;
 36     tree[k].f=0;//删除节点的懒标记
 37 }
 38
 39 void change(int k,int start,int ed,int aim){
 40
 41     if(tree[k].l>=start&&tree[k].r<=ed){
 42         tree[k].color=1<<aim;//颜色种类为1<<aim
 43         tree[k].f=aim;//!!! 懒标记,存下颜色
 44         return ;
 45     }
 46     if(tree[k].f) push_down(k);//!!! 如果当前k编号节点有懒标记,就push_down
 47
 48     int mid=(tree[k].l+tree[k].r)/2;
 49     if(ed<=mid){
 50         change(k*2,start,ed,aim);
 51     }else if(start>mid){
 52         change(k*2+1,start,ed,aim);
 53     }else{
 54         change(k*2,start,ed,aim);
 55         change(k*2+1,start,ed,aim);
 56     }
 57     tree[k].color=(tree[k*2].color|tree[k*2+1].color); //更新父节点颜色数
 58 }
 59
 60 LL ans;
 61
 62 void query(int k,int start,int ed){
 63     if(tree[k].l>=start&&tree[k].r<=ed){
 64         ans=(tree[k].color|ans);
 65         return ;
 66     }
 67
 68     if(tree[k].f) push_down(k);//!!! 同上
 69
 70     int mid=(tree[k].l+tree[k].r)/2;
 71
 72     if(mid>=ed){
 73         query(k*2,start,ed);
 74     }else if(mid<start){
 75         query(k*2+1,start,ed);
 76     }else{
 77         query(k*2,start,ed);
 78         query(k*2+1,start,ed);
 79     }
 80 }
 81
 82 int main(){
 83     memset(tree,0,sizeof(tree));
 84     int L,T,O;
 85     char s;
 86     int A,B,C;
 87     scanf("%d%d%d",&L,&T,&O);
 88     getchar();
 89     BTree(1,1,L);
 90     while(O--){
 91         scanf("%c",&s);
 92
 93         if(s=='C'){
 94             scanf("%d%d%d",&A,&B,&C);
 95             getchar();
 96             change(1,min(A,B),max(A,B),C);
 97         }else if(s=='P'){
 98             scanf("%d%d",&A,&B);
 99             getchar();
100             ans=0;
101             query(1,min(A,B),max(A,B));//!这比较坑,要注意
102             int cnt=0;
103             while(ans){//计算ans的二进制中有几个1
104                 int h=ans%2;
105                 ans>>=1;
106                 if(h) cnt++;
107             }
108             printf("%d\n",cnt);
109         }
110     }
111     return 0;
112 }
113 /*
114 8 20 10
115 C 5 5 5
116 C 6 6 6
117 C 7 7 7
118 C 8 8 8
119 C 5 7 9
120 P 5 8
121 C 4 5 10
122 P 4 7
123 P 4 8
124 P 3 7
125 */

View Code

懒操作的效果演示:

在上面代码改变和询问下都加上树的输出,如下:

    while(O--){scanf("%c",&s);if(s=='C'){scanf("%d%d%d",&A,&B,&C);getchar();change(1,min(A,B),max(A,B),C);//***************演示************ for(int i=1;i<=15;i++){cout<<tree[i].l<<"   "<<tree[i].r<<"   "<<tree[i].color<<endl;}}else if(s=='P'){scanf("%d%d",&A,&B);getchar();ans=0;query(1,min(A,B),max(A,B));//*********************************** for(int i=1;i<=15;i++){cout<<tree[i].l<<"   "<<tree[i].r<<"   "<<tree[i].color<<endl;}int cnt=0;while(ans){//计算ans的二进制中有几个1 int h=ans%2;ans>>=1;if(h) cnt++;}printf("%d\n",cnt);}}

输出如下:

有没有发现在改变颜色时并没有改变全部,而是只改变了相应区间和它的父树,而在询问时却在改变一些本该改变时要改变的区间(也没有全部改变,只改变了与询问区间有关的区间(要是没怎么懂就画图看看吧!)),这样就大大减少了不必要的操作。

转载于:https://www.cnblogs.com/liuzuolin/p/10490900.html

初学线段树(poj3264+poj2777)相关推荐

  1. 【初学线段树,看这篇文章准没错】线段树(单点修改and区间修改)acm寒假集训日记22/1/10

    线段树 线段树是算法竞赛中常用的用来维护区间信息的数据结构.是一名ACMer 需要掌握的一种基础.重要的数据结构线段树可以在O(logN)的时间复杂度内实现单点修改,区间修改,区间查询(区间求和,区间 ...

  2. poj3264 线段树

    再次理解线段树.比第一次又有了更深刻的认识,虽然还不能完全靠自己写出来,但自己慢慢的去摸索,一定能慢慢理解那个套路了.加油,拿下线段树. 同时,推荐北大郭炜讲的线段树. poj3264 #includ ...

  3. Count Color poj2777 线段树

    Count Color poj2777 线段树 题意 有一个长木板,现在往上面在一定区间内刷颜色,后来刷的颜色会掩盖掉前面刷的颜色,问每次一定区间内可以看到多少种颜色. 解题思路 这里使用线段树,因为 ...

  4. POJ3264 Balanced Lineup【线段树】

    Balanced Lineup Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 63040   Accepted: 29405 ...

  5. POJ3264[线段树]

    就是说有一个序列长度为n,q次询问,询问区间内最大值与最小值的差 可以说是经典的线段树板子题.. 第一次写线段树,有些漏洞请大家及时提出. 线段树大概就是将元素分配,蒟蒻语文不好,大家请看图 [图片来 ...

  6. POJ3264——Balanced Lineup(线段树)

    本文出自:http://blog.csdn.net/svitter 题意:在1~200,000个数中.取一段区间.然后在区间中找出最大的数和最小的数字.求这两个数字的差. 分析:按区间取值,非常明显使 ...

  7. poj2777线段树

    题目大意:有一长为L的木板,将其分为等长的L段,从左到右编号为1~L.现将L段木板着色 1. 输入"C A B C" 是将A到B这一段着成C这种颜色. 2. 输入"P A ...

  8. 线段树染色问题(例题为poj2777)

    染色问题加离散化是poj2528,过后我会放出来的 关于离散的详细解释参考博客:https://blog.csdn.net/iwts_24/article/details/81603603 区域染色覆 ...

  9. 需要支持多种操作的线段树该如何确定运算顺序?

    先来看一道最简单的加乘标记: \(\huge\text{点我看题}\) 本题需要我们进行加法,乘法的在线修改以及查询取模后的结果.因为加法和乘法对于取模运算来说是不受限制的,即可以随时在操作过程中进行 ...

  10. poj 3264 Balanced Lineup RMQ问题 线段树

    For the daily milking, Farmer John's N cows (1 ≤ N ≤ 50,000) always line up in the same order. One d ...

最新文章

  1. 路由器桥接以后怎么找到_2个无线路由器之间怎样做到无缝连接?
  2. 多字节 unicode和utf-8的转换
  3. offset client scroll
  4. Android系统服务
  5. 得到ios设备的屏幕尺寸信息
  6. yolov配置之:cuda、 cudnn安装
  7. 利用oc门或od门实现线与_景县专业门球场专用人造草坪甄选博翔远
  8. 事务的四大特性、事务处理开始与结束、v$transactio、 v$LOCK
  9. 最优化理论与方法(part4)--秩一校正
  10. windows系统下_ffmpeg编译_2011年
  11. Nginx的开启和关闭
  12. C语言 完数问题求解
  13. 怎么快速同时给多个 Excel 文档批量添加自定义的文字和图片水印
  14. 如何去掉广告实现百度精准搜索
  15. 正交矩阵和旋转矩阵之间关系和性质总结
  16. excel建立层级_利用Excel升职加薪——数据分析报告
  17. 屏蔽电脑上所有的广告推送(亲测可用)
  18. 荣耀30青春版怎么样?到手后远超预期!
  19. 高维非空间数据可视化
  20. PCIE设备的x1,x4,x8,x16有什么区别?

热门文章

  1. linux串口API编程
  2. 聚合函数和group by
  3. Springboot2.X + screw数据库快速开发文档
  4. Javsscript自定义事件和触发
  5. 经典排序算法(十九)--Flash Sort
  6. C++ 简单的SQL注入过滤
  7. 链表常见算法题总结(Java)
  8. php utc时区设置,php DateTimeZone 设置UTC 无效
  9. 万能点位图软件_万能点位图软件_BoardViewer
  10. 创建者模式 --- 工厂模式