線段樹的學習之:如何用線段樹計算矩形面積(二)
阿新 • • 發佈:2020-10-20
研究了線段樹計算矩形面積的水題poj1151的實現過程,我又好好研究了用線段樹來解決另一道稍微難一點的題:hdu3265,這是09年寧波賽區的一道題,只是在poj1151的基礎上更巧妙了一點!
由於題目給出的矩形是回字形,所以我把把一個回字拆開就可以了,也就是說,hdu3265中插入一個poster,相當於插入了四個矩形(也可能只有三個)!
由於題目的資料量比較小,只有5000,而且是整型,所以不用離散化操作!在面前那道題的基礎上,去掉離散化就OK了!
把程式碼貼出來吧!
- #include<stdio.h>
- #include<algorithm>
- usingnamespace
- #defineM50001
- typedeflonglongllong;
- structTree{
- intll,rr,cover;//這裡的cover之所以用llong而不用bool是因為可能出現多條邊重疊
- intlen;
- }tree[4*M];
- structLine{
- intx,y1,y2;
- intflag;
- }line[8*M];
- //比較函式
- boolcmp(constLine&l1,constLine&l2){//排序的比較函式
- if(l1.x==l2.x){
- returnl1.flag>l2.flag;
- }
- returnl1.x<l2.x;
- }
- //建樹
- voidbuild(intid,intll,intrr){
- tree[id].ll=ll;tree[id].rr=rr;
- tree[id].len=tree[id].cover=0;
- if(ll+1==rr)return;//如果已經是元線段,則不用拆分了
- intmid=(ll+rr)>>1;
- build(id*2,ll,mid);
- build(id*2+1,mid,rr);
- }
- voidlenght(intid){//求用於計算的線段實際長度
- if(tree[id].cover>0){
- tree[id].len=tree[id].rr-tree[id].ll;
- }elseif(tree[id].ll+1==tree[id].rr){//元線段
- tree[id].len=0;
- }else
- tree[id].len=tree[id*2].len+tree[id*2+1].len;
- }
- //更新樹
- voidupdate(intid,Lineline){
- if(tree[id].ll==line.y1&&tree[id].rr==line.y2){
- tree[id].cover+=line.flag;
- lenght(id);
- return;
- //這裡的比較有點特別,值得關注一下,
- }elseif(line.y1>=tree[2*id+1].ll){//用右孩子的左邊界來比較
- update(id*2+1,line);
- }elseif(line.y2<=tree[id*2].rr){//左孩子的右邊界來比較
- update(id*2,line);
- }else{//分跨兩個邊界
- Linetmp;
- tmp=line;tmp.y2=tree[id*2].rr;
- update(2*id,tmp);
- tmp=line;tmp.y1=tree[id*2+1].ll;
- update(2*id+1,tmp);
- }
- lenght(id);//回溯的時候修改,使根結點的len實時更新
- }
- voidload(intid,intx,inty1,inty2,intflag){
- line[id].x=x;
- line[id].y1=y1;
- line[id].y2=y2;
- line[id].flag=flag;
- }
- intmain(){
- intn;
- inti,t;
- intMax=1;
- while(scanf("%d",&n)&&n){
- intx1,y1,x2,y2,x3,y3,x4,y4;
- for(t=i=1;i<=n;i++){
- scanf("%d%d%d%d%d%d%d%d",&x1,&y1,&x2,&y2,&x3,&y3,&x4,&y4);
- ++x1,++y1,++x2,++y2,++x3,++y3,++x4,++y4;//由於起點是0,而線段樹管理的起點是1,所以整體平移一個單位
- if(x1<x3&&y1<y2){
- load(t,x1,y1,y2,1);t++;
- load(t,x3,y1,y2,-1);t++;
- Max=max(Max,max(y1,y2));
- }
- if(x3<x4&&y4<y2){
- load(t,x3,y4,y2,1);t++;
- load(t,x4,y4,y2,-1);t++;
- Max=max(Max,max(y4,y2));
- }
- if(x3<x4&&y1<y3){
- load(t,x3,y1,y3,1);t++;
- load(t,x4,y1,y3,-1);t++;
- Max=max(Max,max(y1,y3));
- }
- if(x4<x2&&y1<y2){
- load(t,x4,y1,y2,1);t++;
- load(t,x2,y1,y2,-1);t++;
- Max=max(Max,max(y1,y2));
- }
- }
- sort(line+1,line+t,cmp);
- build(1,1,Max);//用y的最大值來建樹
- update(1,line[1]);//第一條邊一定是入邊
- llongans=0;
- for(i=2;i<t;i++){
- ans+=(llong)tree[1].len*(llong)(line[i].x-line[i-1].x);
- update(1,line[i]);//最後一條邊肯定是出邊,不用考慮
- }
- printf("%I64d\n",ans);
- }
- return0;
- }
經過這幾天的思考,我對線段樹的認識也更深入了一些!我們用線段樹解決矩形相關的問題時,容易走進一個誤區,那就是矩形是二維的,而通常我們學習的線段樹是一維的!用一維的線段樹來操作二維的區間,是很難讓人想通其中的細節!
但實際上,線段樹操作的依然是一維的!這就需要我們把線段樹操作線段(比如線段著色)的細節弄清楚!從我部落格中關於線段樹的兩道題可以看出,線段樹管理的只是X軸或者y軸的線段,
怎麼管理呢?線段的插入或者刪除!所以我理解到的用線段樹線段樹求矩形面積的本質就是:用線段樹來插入或者刪除線段,再本質一點:線段樹的更新區間操作!
再回到矩形的面積,用線段樹其實用到了一種分割的思想,把原來的三個矩形分割成了5個矩形,X軸排序後,矩形的高是很容易求出來的,我們可以不管,但是矩形的寬度就不好求,這時候,線段樹的作用就凸顯出來了!所以線段樹在這裡,本質上還是管理線段,而不是矩形!
轉載於:https://blog.51cto.com/sbp810050504/1048803