1. 程式人生 > 實用技巧 >線段樹的學習之:如何用線段樹計算矩形面積(二)

線段樹的學習之:如何用線段樹計算矩形面積(二)

研究了線段樹計算矩形面積的水題poj1151的實現過程,我又好好研究了用線段樹來解決另一道稍微難一點的題:hdu3265,這是09年寧波賽區的一道題,只是在poj1151的基礎上更巧妙了一點!

由於題目給出的矩形是回字形,所以我把把一個回字拆開就可以了,也就是說,hdu3265中插入一個poster,相當於插入了四個矩形(也可能只有三個)!

由於題目的資料量比較小,只有5000,而且是整型,所以不用離散化操作!在面前那道題的基礎上,去掉離散化就OK了!

把程式碼貼出來吧!

  1. #include<stdio.h>
  2. #include<algorithm>
  3. usingnamespace
    std;
  4. #defineM50001
  5. typedeflonglongllong;
  6. structTree{
  7. intll,rr,cover;//這裡的cover之所以用llong而不用bool是因為可能出現多條邊重疊
  8. intlen;
  9. }tree[4*M];
  10. structLine{
  11. intx,y1,y2;
  12. intflag;
  13. }line[8*M];
  14. //比較函式
  15. boolcmp(constLine&l1,constLine&l2){//排序的比較函式
  16. if(l1.x==l2.x){
  17. returnl1.flag>l2.flag;
  18. }
  19. returnl1.x<l2.x;
  20. }
  21. //建樹
  22. voidbuild(intid,intll,intrr){
  23. tree[id].ll=ll;tree[id].rr=rr;
  24. tree[id].len=tree[id].cover=0;
  25. if(ll+1==rr)return;//如果已經是元線段,則不用拆分了
  26. intmid=(ll+rr)>>1;
  27. build(id*2,ll,mid);
  28. build(id*2+1,mid,rr);
  29. }
  30. voidlenght(intid){//求用於計算的線段實際長度
  31. if(tree[id].cover>0){
  32. tree[id].len=tree[id].rr-tree[id].ll;
  33. }elseif(tree[id].ll+1==tree[id].rr){//元線段
  34. tree[id].len=0;
  35. }else
  36. tree[id].len=tree[id*2].len+tree[id*2+1].len;
  37. }
  38. //更新樹
  39. voidupdate(intid,Lineline){
  40. if(tree[id].ll==line.y1&&tree[id].rr==line.y2){
  41. tree[id].cover+=line.flag;
  42. lenght(id);
  43. return;
  44. //這裡的比較有點特別,值得關注一下,
  45. }elseif(line.y1>=tree[2*id+1].ll){//用右孩子的左邊界來比較
  46. update(id*2+1,line);
  47. }elseif(line.y2<=tree[id*2].rr){//左孩子的右邊界來比較
  48. update(id*2,line);
  49. }else{//分跨兩個邊界
  50. Linetmp;
  51. tmp=line;tmp.y2=tree[id*2].rr;
  52. update(2*id,tmp);
  53. tmp=line;tmp.y1=tree[id*2+1].ll;
  54. update(2*id+1,tmp);
  55. }
  56. lenght(id);//回溯的時候修改,使根結點的len實時更新
  57. }
  58. voidload(intid,intx,inty1,inty2,intflag){
  59. line[id].x=x;
  60. line[id].y1=y1;
  61. line[id].y2=y2;
  62. line[id].flag=flag;
  63. }
  64. intmain(){
  65. intn;
  66. inti,t;
  67. intMax=1;
  68. while(scanf("%d",&n)&&n){
  69. intx1,y1,x2,y2,x3,y3,x4,y4;
  70. for(t=i=1;i<=n;i++){
  71. scanf("%d%d%d%d%d%d%d%d",&x1,&y1,&x2,&y2,&x3,&y3,&x4,&y4);
  72. ++x1,++y1,++x2,++y2,++x3,++y3,++x4,++y4;//由於起點是0,而線段樹管理的起點是1,所以整體平移一個單位
  73. if(x1<x3&&y1<y2){
  74. load(t,x1,y1,y2,1);t++;
  75. load(t,x3,y1,y2,-1);t++;
  76. Max=max(Max,max(y1,y2));
  77. }
  78. if(x3<x4&&y4<y2){
  79. load(t,x3,y4,y2,1);t++;
  80. load(t,x4,y4,y2,-1);t++;
  81. Max=max(Max,max(y4,y2));
  82. }
  83. if(x3<x4&&y1<y3){
  84. load(t,x3,y1,y3,1);t++;
  85. load(t,x4,y1,y3,-1);t++;
  86. Max=max(Max,max(y1,y3));
  87. }
  88. if(x4<x2&&y1<y2){
  89. load(t,x4,y1,y2,1);t++;
  90. load(t,x2,y1,y2,-1);t++;
  91. Max=max(Max,max(y1,y2));
  92. }
  93. }
  94. sort(line+1,line+t,cmp);
  95. build(1,1,Max);//用y的最大值來建樹
  96. update(1,line[1]);//第一條邊一定是入邊
  97. llongans=0;
  98. for(i=2;i<t;i++){
  99. ans+=(llong)tree[1].len*(llong)(line[i].x-line[i-1].x);
  100. update(1,line[i]);//最後一條邊肯定是出邊,不用考慮
  101. }
  102. printf("%I64d\n",ans);
  103. }
  104. return0;
  105. }

經過這幾天的思考,我對線段樹的認識也更深入了一些!我們用線段樹解決矩形相關的問題時,容易走進一個誤區,那就是矩形是二維的,而通常我們學習的線段樹是一維的!用一維的線段樹來操作二維的區間,是很難讓人想通其中的細節!

但實際上,線段樹操作的依然是一維的!這就需要我們把線段樹操作線段(比如線段著色)的細節弄清楚!從我部落格中關於線段樹的兩道題可以看出,線段樹管理的只是X軸或者y軸的線段,

怎麼管理呢?線段的插入或者刪除!所以我理解到的用線段樹線段樹求矩形面積的本質就是:用線段樹來插入或者刪除線段,再本質一點:線段樹的更新區間操作!

再回到矩形的面積,用線段樹其實用到了一種分割的思想,把原來的三個矩形分割成了5個矩形,X軸排序後,矩形的高是很容易求出來的,我們可以不管,但是矩形的寬度就不好求,這時候,線段樹的作用就凸顯出來了!所以線段樹在這裡,本質上還是管理線段,而不是矩形!

轉載於:https://blog.51cto.com/sbp810050504/1048803