今天讲的扫描线,树状数组,并查集还有前缀树。

  

  扫描线

   扫描线的思路:使用一条垂直于X轴的直线,从左到右来扫描这个图形,明显,只有在碰到矩形的左边界或者右边界的时候,这个线段所扫描到的情况才会改变,所以把所有矩形的入边,出边按X值排序。然后根据X值从小到大去处理,就可以用线段树来维护扫描到的情况。

  如果碰到矩形的入边,就把这条边加入,如果碰到出边,就拿走。

  用根结点记录被覆盖的总长度     更新

  插入数据的顺序:

     将矩形的纵边从左到右排序,然后依次将这些纵边插入线段树。要记住哪些纵边是一个 矩形的左边(开始边),哪些纵边是一个矩形 的右边(结束边),以便插入时,对Len(当前,本区间上有多长的 部分是落在那些矩形中的)和 Covers(本区间当前被多少个矩形 完全包含)做不同的修改。 插入一条边后,就根据根节点的Len 值增加总 覆盖面积的值。 增量是Len * 本边到下一条边的距离。  一开始,所有区间 Len = 0 Covers = 0

  扫描线和线段树推荐一个博客:https://www.cnblogs.com/AC-King/p/7789013.html

例题:POJ 1151 Atlantis

  1. #include <iostream>
  2. #include <algorithm>
  3. #include <math.h>
  4. #include <set>
  5. using namespace std;
  6. double y[];
  7. struct CNode
  8. {
  9. int L,R;
  10. CNode * pLeft, * pRight;
  11. double Len; //当前,本区间上有多长的部分是落在那些矩形中的
  12. int Covers;//本区间当前被多少个矩形完全包含
  13. };
  14. CNode Tree[];
  15. struct CLine
  16. {
  17. double x,y1,y2;
  18. bool bLeft; //是否是矩形的左边
  19. } lines[];
  20. int nNodeCount = ;
  21. bool operator< ( const CLine & l1,const CLine & l2)
  22. {
  23. return l1.x < l2.x;
  24. }
  25. template <class F,class T>
  26. F bin_search(F s, F e, T val)
  27. {
  28. //在区间[s,e)中查找 val,找不到就返回 e
  29. F L = s;
  30. F R = e-;
  31. while(L <= R )
  32. {
  33. F mid = L + (R-L)/;
  34. if( !( * mid < val || val < * mid ))
  35. return mid;
  36. else if(val < * mid)
  37. R = mid - ;
  38. else
  39. L = mid + ;
  40. }
  41. return e;
  42. }
  43. int Mid(CNode * pRoot)
  44. {
  45. return (pRoot->L + pRoot->R ) >>;
  46. }
  47. void Insert(CNode * pRoot,int L, int R)
  48. //在区间pRoot 插入矩形左边的一部分或全部,该左边的一部分或全部覆盖了区间[L,R]
  49. {
  50. if( pRoot->L == L && pRoot->R == R)
  51. {
  52. pRoot->Len = y[R+] - y[L];
  53. pRoot->Covers ++;
  54. return;
  55. }
  56. if( R <= Mid(pRoot))
  57. Insert(pRoot->pLeft,L,R);
  58. else if( L >= Mid(pRoot)+)
  59. Insert(pRoot->pRight,L,R);
  60. else
  61. {
  62. Insert(pRoot->pLeft,L,Mid(pRoot));
  63. Insert(pRoot->pRight,Mid(pRoot)+,R);
  64. }
  65. if( pRoot->Covers == ) //如果不为0,则说明本区间当前仍然被某个矩形完全包含,则不能更新 Len
  66. pRoot->Len = pRoot->pLeft ->Len + pRoot->pRight ->Len;
  67. }
  68. void Delete(CNode * pRoot,int L, int R)
  69. {
  70. //在区间pRoot 删除矩形右边的一部分或全部,该矩形右边的一部分或全部覆盖了区间[L,R]
  71. if( pRoot->L == L && pRoot->R == R)
  72. {
  73. pRoot->Covers --;
  74. if( pRoot->Covers == )
  75. if( pRoot->L == pRoot->R )
  76. pRoot->Len = ;
  77. else
  78. pRoot->Len = pRoot->pLeft ->Len + pRoot->pRight ->Len;
  79. return ;
  80. }
  81. if( R <= Mid(pRoot))
  82. Delete(pRoot->pLeft,L,R);
  83. else if( L >= Mid(pRoot)+)
  84. Delete(pRoot->pRight,L,R);
  85. else
  86. {
  87. Delete(pRoot->pLeft,L,Mid(pRoot));
  88. Delete(pRoot->pRight,Mid(pRoot)+,R);
  89. }
  90. if( pRoot->Covers == ) //如果不为0,则说明本区间当前仍然被某个矩形完全包含,则不能更新 Len
  91. pRoot->Len = pRoot->pLeft ->Len + pRoot->pRight ->Len;
  92. }
  93. void BuildTree( CNode * pRoot, int L,int R)
  94. {
  95. pRoot->L = L;
  96. pRoot->R = R;
  97. pRoot->Covers = ;
  98. pRoot->Len = ;
  99. if( L == R)
  100. return;
  101. nNodeCount ++;
  102. pRoot->pLeft = Tree + nNodeCount;
  103. nNodeCount ++;
  104. pRoot->pRight = Tree + nNodeCount;
  105. BuildTree( pRoot->pLeft,L,(L+R)/);
  106. BuildTree( pRoot->pRight,(L+R)/+,R);
  107. }
  108. int main()
  109. {
  110. int n;
  111. int i,j,k;
  112. double x1,y1,x2,y2;
  113. int yc,lc;
  114. int nCount = ;
  115. int t = ;
  116. while(true)
  117. {
  118. scanf("%d",&n);
  119. if( n == ) break;
  120. t ++;
  121. yc = lc = ;
  122. for( i = ; i < n; i ++ )
  123. {
  124. scanf("%lf%lf%lf%lf", &x1, &y1,&x2,&y2);
  125. y[yc++] = y1;
  126. y[yc++] = y2;
  127. lines[lc].x = x1;
  128. lines[lc].y1 = y1;
  129. lines[lc].y2 = y2;
  130. lines[lc].bLeft = true;
  131. lc ++;
  132. lines[lc].x = x2;
  133. lines[lc].y1 = y1;
  134. lines[lc].y2 = y2;
  135. lines[lc].bLeft = false;
  136. lc ++;
  137. }
  138. sort(y,y + yc);
  139. yc = unique(y,y+yc) - y;
  140. nNodeCount = ;
  141. //yc 是横线的条数,yc- 1是纵向区间的个数,这些区间从0
  142. //开始编号,那么最后一个区间
  143. //编号就是yc - 1 -1
  144. BuildTree(Tree, , yc - - );
  145. sort(lines,lines + lc);
  146. double Area = ;
  147. for( i = ; i < lc - ; i ++ )
  148. {
  149. int L = bin_search( y,y+yc,lines[i].y1) - y;
  150. int R = bin_search( y,y+yc,lines[i].y2) - y;
  151. if( lines[i].bLeft )
  152. Insert(Tree,L,R-);
  153. else
  154. Delete(Tree,L,R-);
  155. Area += Tree[].Len * (lines[i+].x - lines[i].x);
  156. }
  157. printf("Test case #%d\n",t);
  158. printf("Total explored area: %.2lf\n",Area);
  159. printf("\n",Area);
  160. }
  161. return ;
  162. }

老师上课讲的代码

  树状数组

  只能解决单点更新、区间求和问题。

  可以快速求出任意区间和。

  能力比线段树弱。  它能解决的问题线段树都能解决,是线段树能解决的问题的子集。

  它的好处: 1.写起来简单    2.效率高(常数小)        ps:线段树常数大       两者区间查询的时间复杂度都是O(logn)

  三个重要的函数 :

  1. int lowerbit(int x)
  2. {
  3. return x&-x;
  4. }
  5.  
  6. void Update(int i,int v) // 初始化与单点修改
  7. {
  8. while(i <= n)
  9. {
  10. c[i] += v ;
  11. i += lowbit(i) ;
  12. }
  13. }
  14.  
  15. int Sum(int i) // 区间求和
  16. {
  17. int res = ;
  18. while(i)
  19. {
  20. res += c[i] ;
  21. i -= lowbit(i) ;
  22. }
  23. return res ;
  24. }

  

  lowbit(x): 只保留x的二进制最右边的1,其余位都变为0后的值

  对于序列a,我们设一个数组C   C[i] = a[i – 2 k + 1] + … + a[i]   C即为a的树状数组

  k为i在二进制下末尾0的个数    2k就是i 保留最右边的1,其余位全变0

   i从1开始算!

  

  C[i] = a[i-lowbit(i)+1] + …+ a[i] C包含哪些项看上去没有规律

  C1=A1

  C2=A1+A2

  C3=A3

  C4=A1+A2+A3+A4

  C5=A5

  C6=A5+A6

  C7=A7

  C8=A1+A2+A3+A4+A5+A6+A7+A8

   …………

  C16=A1+A2+A3+A4+A5+A6+A7+A8+A9+A10+ A11+A12+A13+A14+A15+A16

  树状数组图示

  将C[]数组的结点序号转化为二进制

  1=(001)      C[1]=A[1];
  2=(010)      C[2]=A[1]+A[2];
  3=(011)      C[3]=A[3];
  4=(100)      C[4]=A[1]+A[2]+A[3]+A[4];
  5=(101)      C[5]=A[5];
  6=(110)      C[6]=A[5]+A[6];
  7=(111)      C[7]=A[7];
  8=(1000)    C[8]=A[1]+A[2]+A[3]+A[4]+A[5]+A[6]+A[7]+A[8];
  对照式子可以发现  C[i]=A[i-2^k+1]+A[i-2^k+2]+......A[i]; (k为i的二进制中从最低位到高位连续零的长度)

  时间复杂度:建数组: O(n) 更新: O(logn) 局部求和:O(logn)

  树状数组推荐博客:https://www.cnblogs.com/ECJTUACM-873284962/p/6380245.html

  POJ题目推荐: 2182, 2352, 1177, 3667,3067

  并查集

  3个操作:

  1.合并两个集合

  2.查询一个元素在哪个集合

  3.查询两个元素是否属于同一集合

  核心:查一个元素的树根(时间复杂度为常数)

  实际应用代码:

  1. int par[];
  2. int GET_ROOT(int a)//查询一个元素在哪个集合 路径压缩
  3. {
  4. if (par[a]!=a)
  5. par[a] = GET_ROOT(par[a]);
  6. return par[a];
  7. }
  8. int query(int a,int b)//查询两个元素是否属于同一集合
  9. {
  10. return GET_ROOT(a)==GET_ROOT(b);
  11. }
  12. void merge(int a,int b)//合并两个集合
  13. {
  14. par[GET_ROOT(a)] = GET_ROOT(b);
  15. }

  有时候需要添加数组记录,例如添加sum数组记录每个集合有多少元素。

  在合并时进行维护

  做题时主要理清怎样算同一集合。

例题:1.POJ 1611 The Suspects

   2.POJ 1988 Cube Stacking

   3.POJ 1182 食物链

  DFA(一部分)

    多模式匹配

  trie图是一种DFA,可以由trie树为基础构造出来, 对于插入的每个模式串,其插入过程中使用的最后一 个节点都作为DFA的一个终止节点。 如果要求一个母串包含哪些模式串,以用母串作为 DFA的输入,在DFA 上行走,走到终止节点,就意 味着匹配了相应的模式串(没能走到终止节点,并不 意味着一定不包含模式串)。

  避免母串指针回溯  -->  记住哪些模式串的哪个前缀已经被匹配  -->前缀指针

  前缀指针:仿照KMP算法的Next数组, 我们也对树上的每一个节点 建立一个前缀指针。这个前 缀指针的定义和KMP算法中 的next数组相类似,从根节 点沿边到节点p我们可以得 到一个字符串S,节点p的前 缀指针定义为:指向树中出 现过的S的最长的后缀(不能等于S)。

ACM北大暑期课培训第五天的更多相关文章

  1. ACM北大暑期课培训第一天

    今天是ACM北大暑期课开课的第一天,很幸运能参加这次暑期课,接下来的几天我将会每天写博客来总结我每天所学的内容.好吧下面开始进入正题: 今天第一节课,郭炜老师给我们讲了二分分治贪心和动态规划. 1.二 ...

  2. ACM北大暑期课培训第七天

    昨天没时间写,今天补下. 昨天学的强连通分支,桥和割点,基本的网络流算法以及Dinic算法: 强连通分支 定义:在有向图G中,如果任意两个不同的顶点 相互可达,则称该有向图是强连通的. 有向图G的极大 ...

  3. ACM北大暑期课培训第六天

    今天讲了DFA,最小生成树以及最短路 DFA(接着昨天讲) 如何高效的构造前缀指针: 步骤为:根据深度一一求出每一个节点的前缀指针.对于当前节点,设他的父节点与他的边上的字符为Ch,如果他的父节点的前 ...

  4. ACM北大暑期课培训第二天

    今天继续讲的动态规划 ... 补充几个要点: 1. 善于利用滚动数组(可减少内存,用法与计算方向有关) 2.升维 3.可利用一些数据结构等方法使代码更优  (比如优先队列) 4.一般看到数值小的 (十 ...

  5. ACM北大暑期课培训第八天

    今天学了有流量下界的网络最大流,最小费用最大流,计算几何. 有流量下界的网络最大流 如果流网络中每条边e对应两个数字B(e)和C(e), 分别表示该边上的流量至少要是B(e),最多 C(e),那么,在 ...

  6. ACM北大暑期课培训第四天

    今天讲了几个高级搜索算法:A* ,迭代加深,Alpha-Beta剪枝   以及线段树 A*算法 启发式搜索算法(A算法) : 在BFS算法中,若对每个状态n都设定估价函数 f(n)=g(n)+h(n) ...

  7. ACM北大暑期课培训第三天

    今天讲的内容是深搜和广搜 深搜(DFS) 从起点出发,走过的点要做标记,发现有没走过的点,就随意挑一个往前走,走不 了就回退,此种路径搜索策略就称为“深度优先搜索”,简称“深搜”. bool Dfs( ...

  8. 牛客网暑期ACM多校训练营(第五场):F - take

    链接:牛客网暑期ACM多校训练营(第五场):F - take 题意: Kanade有n个盒子,第i个盒子有p [i]概率有一个d [i]大小的钻石. 起初,Kanade有一颗0号钻石.她将从第1到第n ...

  9. 2019暑期北航培训—预培训作业-IDE的安装与初步使用(Visual Studio版)

    这个作业属于那个课程 2019北航软件工程暑期师资培训 这个作业要求在哪里 预培训-IDE的安装与初步使用(Visual Studio版) 我在这个课程的目标是 提高自身实际项目实践能力,掌握帮助学生 ...

随机推荐

  1. springmvc 返回json数据给前台jsp页面展示

    spring mvc返回json字符串的方式 方案一:使用@ResponseBody 注解返回响应体 直接将返回值序列化json            优点:不需要自己再处理 步骤一:在spring- ...

  2. Python--day71--分页功能

    分页功能代码: from django.shortcuts import render # Create your views here. from app01 import models def b ...

  3. 关于top命令

    top命令交互操作指令 下面列出一些常用的 top命令操作指令 q:退出top命令 :立即刷新 s:设置刷新时间间隔 c:显示命令完全模式 t::显示或隐藏进程和CPU状态信息 m:显示或隐藏内存状态 ...

  4. @JsonIgnore @JsonIdentityInfo 处理Hibernate 循环引用的问题

    enterprise和user一对一的关系: @Entity @Table(name = "enterprise") public class Enterprise extends ...

  5. 给websocket加入心跳包防止自动断开连接

    var userId=$("#userId").val(); var lockReconnect = false; //避免ws重复连接 var ws = null; // 判断当 ...

  6. CF351E Jeff and Permutation

    CF351E Jeff and Permutation 贪心好题 考虑每个对能否最小化贡献和 先不考虑绝对值相同情况 发现,对于a,b假设|a|<|b|,那么有无贡献只和b的正负有关!如果a在b ...

  7. python基础七之copy

    浅拷贝 没有嵌套,则copy后完全不同,有嵌套,则copy后本体不同,嵌套相同. l1 = [1, 2, [4, 5, 6], 3] l2 = l1.copy() print(l1 is l2) # ...

  8. PhpStorm terminal无法输入命令的解决方法

    下面小编就为大家带来一篇PhpStorm terminal无法输入命令的解决方法.小编觉得挺不错的,现在就分享给大家,也给大家做个参考.一起跟随小编过来看看吧   在使用PhpStorm时,点击下面的 ...

  9. 提前终止forEach技巧,使用try catch

    学习react优化性能的时候,在render之前,生命周期shouldComponentUpdate里判断前后两次数据是否一致,使用了forEach嵌套if语句,如果满足条件想直接break跳出for ...

  10. 修改github上的项目语言类型

    当在github上上传一个项目时,可能会出现一个问题就是项目代码类型是自动生成的,可能与我们实际项目代码种类不匹配,此时就需要修改项目语言类型了. 由于无法直接更改,所以用到此方法: 在你的项目根目录 ...