NOIP--OI省选算法之线段树(1)

线段树算法

线段树的构造思想 
线段树是一棵二叉树,树中的每一个结点表示了一个区间[a,b]。每一个叶子节点表示了一个单位区间。对于每一个非叶结点所表示的结点[a,b],其左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2,b]。 
例如: 

线段树的运用 
线段树的每个节点上往往都增加了一些其他的域。在这些域中保存了某种动态维护的信息,视不同情况而定。这些域使得线段树具有极大的灵活性,可以适应不同的需求。 

例1:求覆盖线段的总长度 
      [10000,22000]   [30300,55000]   [44000,60000]   [55000,60000] 
排序得10000,22000,30300,44000,55000,60000 
对应得     1,   2,    3,    4,    5,    6 
        [1,2]            [3,5]           [4,6]          [5,6] 

线段树做法: 



例2:http://acm.pku.edu.cn/JudgeOnline/problem?id=2528 
题意:贴海报,输出可以看到的个数. 

  1. //贴海报,输出没有被覆盖的个数  

  2. //可以改成cpp试试  

  3. #include<stdio.h>  

  4. struct node  

  5. {  

  6.     int l,r; //左右孩子的编号  

  7.     int st,mi,en;  

  8.     int id;  

  9. };  // 线段树简单一维  

  10. const int maxN = 50000002; //线段树的节点个数  

  11. const int maxL = 10000020; //台阶的宽度上限  

  12.   

  13. node segment_tree[maxN]; //保存着线段树的所有节点  

  14. #define tree segment_tree  

  15. int root, ptr;  

  16. void insert(int cr, int start, int end, int color) //插入到指定区域,同时初始化沿途的所有节点  

  17. {  

  18.     if(start >= end) //不符合输入要求  

  19.         return;  

  20.     if(tree[cr].st == start && tree[cr].en == end) //输入区间正好等于节点的表示区间  

  21.     {  

  22.         tree[cr].id = color; //这个区间属于该海报  

  23.         return;  

  24.     }  

  25.     int mid = (tree[cr].st + tree[cr].en) / 2;  

  26.     if(tree[cr].l == 0) //意味着还没有初始化孩子结点  

  27.     {  

  28.         //ptr代表节点的编号  

  29.         tree[cr].l = ptr++;  

  30.         tree[tree[cr].l].l = tree[tree[cr].l].r = 0;  

  31.         tree[tree[cr].l].id = -1;  

  32.         tree[tree[cr].l].st = tree[cr].st, //这里对左右孩子的范围进行初始化  

  33.         tree[tree[cr].l].en = mid;  

  34.     }  

  35.     if(tree[cr].r == 0)  

  36.     {  

  37.         tree[cr].r = ptr++;  

  38.         tree[tree[cr].r].l = tree[tree[cr].r].r = 0;  

  39.         tree[tree[cr].r].id = -1;  

  40.         tree[tree[cr].r].st = mid,  

  41.         tree[tree[cr].r].en = tree[cr].en;  

  42.     }  

  43.   

  44.     if(tree[cr].id != 0) //之后的子区间肯定都属于该海报  

  45.     {  

  46.         tree[tree[cr].l].id = tree[tree[cr].r].id = tree[cr].id;  

  47.         tree[cr].id = 0;  

  48.     }  

  49.   

  50.     if(start >= mid){  

  51.         insert(tree[cr].r, start, end, color);  

  52.         return;  

  53.     }  

  54.     if(end <= mid){  

  55.         insert(tree[cr].l, start, end, color);  

  56.         return;  

  57.     }  

  58.     insert(tree[cr].l, start, mid, color);  

  59.     insert(tree[cr].r, mid, end, color);  

  60. }  

  61.   

  62.   

  63. char exist[10001];  

  64. void trail(int cr) //统计可以看见的节点编号  

  65. {  

  66.     if(cr == 0 || tree[cr].id == -1)  

  67.         return;  

  68.     exist[tree[cr].id] = 1; //id不为0,意味着只有它可见,但之后的节点都看不见了.  

  69.     if(tree[cr].id != 0) //不为0意味着后面的区域都要被覆盖.  

  70.         return;  

  71.     trail(tree[cr].l);  

  72.     trail(tree[cr].r);  

  73. }  

  74.   

  75. //初始化跟节点  

  76. void init()  

  77. {  

  78.     root = 1;  

  79.     tree[root].l = tree[root].r = tree[root].id = 0;  

  80.     tree[root].st = 1, tree[root].en = maxL, tree[root].mi = (1 + maxL)/2;  

  81.     ptr = 2;  

  82. }  

  83.   

  84. int main()  

  85. {  

  86.     int test,n,i,l,r;  

  87.     scanf("%d", &test);  

  88.     while(test--)  

  89.     {  

  90.         init();  

  91.         scanf("%d",&n);  

  92.         for(i = 1; i <= n; i++)  

  93.         {  

  94.             scanf("%d%d",&l,&r);  

  95.             insert(1, l, r+1, i); //从根节点开始插入  

  96.         }  

  97.         for(i = 1; i <= n; i++)  

  98.             exist[i] = 0;  

  99.         trail(1);  

  100.         int ans = 0;  

  101.         for(i = 1; i <= n; i++)  

  102.             if(exist[i])  

  103.                 ans++;  

  104.         printf("%d\n",ans);  

  105.     }  

  106.     return 0;  

  107. }  

 

RMQ (Range Minimum/Maximum Query)问题是指:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在[i,j]里的最小(大)值,也就是说,RMQ问题是指求区间最值的问题 

主要方法及复杂度(处理复杂度和查询复杂度)如下: 
1.朴素(即搜索) O(n)-O(n) 
2.线段树(segment tree) O(n)-O(qlogn) 
3.ST(实质是动态规划) O(nlogn)-O(1) 

 

NOIP信息学视频地址

视频地址

链接:https://pan.baidu.com/s/1tHo1DFMaDuMZAemNH60dmw 
提取码:7jgr

posted @ 2020-10-30 13:27  tianli3151  阅读(164)  评论(0编辑  收藏  举报