日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

ACM-线段树扫描线总结

發布時間:2025/7/25 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ACM-线段树扫描线总结 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

掃描線的基礎概念可以看這幾篇文章

http://blog.csdn.net/xingyeyongheng/article/details/8927732

http://www.cnblogs.com/scau20110726/archive/2013/03/21/2972808.html

?

在這里講一下應用

?

1.求矩形的面積并

以hdu 1542為例:

給出n個矩陣的對角坐標,求矩陣的面積并。

?

這里需要注意這么幾點:

  • 離散化

因為數據跨度很大,所以肯定要將數據離散化,保存在hash數組中。但要小心離散化本身的坑。

離散化的坑,就是在表示線段樹時,我們會把1-4劃分為1-2和3-4,如果覆蓋1-2和3-4就等同于把整個線段覆蓋了,但其實不對,因為2和3中間也是有值的。

?下面這題也遇到這個坑,解決方法是:在update時讀入r-1,計算時使用r+1

  • 水平掃描的話,hash的是y坐標,反之亦然
  • hash最好要去重

?

#include <iostream> #include <string> #include <cstring> #include <cstdlib> #include <cstdio> #include <cmath> #include <algorithm> #include <stack> #include <queue> #include <cctype> #include <vector> #include <iterator> #include <set> #include <map> #include <sstream> using namespace std;#define mem(a,b) memset(a,b,sizeof(a)) #define pf printf #define sf scanf #define spf sprintf #define pb push_back #define debug printf("!\n") #define MAXN 200+5 #define MAX(a,b) a>b?a:b #define blank pf("\n") #define LL long long #define ALL(x) x.begin(),x.end() #define INS(x) inserter(x,x.begin()) #define pqueue priority_queue #define INF 0x3f3f3f3f#define ls (rt<<1) #define rs (rt<<1|1)int n,m;double hh[MAXN],col[MAXN<<2],len[MAXN<<2];struct node {double l,r,x,c;node(){}node(double a,double b,double c,double d):l(a),r(b),x(c),c(d){}bool operator < (const node &b) const{return x<b.x;} }a[MAXN];void PushUp(int rt,int l,int r) {if(col[rt]){len[rt] = hh[r+1] - hh[l];}else if(l==r) len[rt] = 0;else{len[rt] = len[ls]+len[rs];} }void update(int val,int L,int R,int l,int r,int rt) {if(L<=l && r<=R){col[rt] += val;PushUp(rt,l,r);return;}int mid = (l+r)>>1;if(L <= mid) update(val,L,R,l,mid,ls);if(R > mid) update(val,L,R,mid+1,r,rs);PushUp(rt,l,r); }int main() {int i,j,k,t,kase=1;while(~sf("%d",&n) && n){int v=0;double sum = 0;for(i=0;i<n;i++){double x1,y1,x2,y2;sf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);hh[v] = y1;a[v++] = node(y1,y2,x1,1);hh[v] = y2;a[v++] = node(y1,y2,x2,-1);}sort(hh,hh+v);sort(a,a+v);int d = 1;for(i=1;i<v;i++){if(hh[i]!=hh[i-1]) hh[d++] = hh[i];}mem(len,0);mem(col,0);for(i=0;i<v-1;i++){int l = lower_bound(hh,hh+d,a[i].l)-hh;int r = lower_bound(hh,hh+d,a[i].r)-hh-1;update(a[i].c,l,r,0,d-1,1);sum+=len[1]*(a[i+1].x-a[i].x);//pf("%lf %lf\n",sum,len[1]); }pf("Test case #%d\nTotal explored area: %.2lf\n\n",kase++,sum);}return 0; }

?

2.求矩形周長并

以hdu 1828為例:

http://blog.csdn.net/xingyeyongheng/article/details/8931410

http://www.cnblogs.com/scau20110726/archive/2013/04/13/3018687.html

http://www.cnblogs.com/scau20110726/archive/2013/04/13/3018702.html

三篇很不錯的文章,基礎概念可以看這幾個

?

從面積變成了周長,方法其實是差不多的。解決這個問題有兩種思路:

  • 對橫線,豎線分別做一次處理。分別求出有效覆蓋長度,兩者之和就是結果。
    • 把矩形分成橫線和豎線去處理,可知是完全相同的操作,我們來講下怎么算出橫線部分,豎線部分就是照搬即可。

      將橫線保存在一個表中,按橫線所處的豎直位置排序(升序),另外每條橫線帶一個標記值,原矩形的下線為1,上線為-1(對應過去就是插入線段和刪除線段)

      從低到高掃描橫線,沒掃到一條橫線就能計算出一部分橫線值。計算方法是算出現在總區間的被覆蓋的長度,然后求出與上一次的總區間的覆蓋長度的差(即相減求絕對值),因為每次添加了一條線段,如果沒有沒有使總區間覆蓋長度發生變化,說明這條線段其實在多邊形的內部,被覆蓋掉了,不能計算,只要能引起總區間長度發生變化的,說明該線段不被覆蓋不被包含,要計算

      而豎線部分的做法是一樣的,把豎線保存在一個表中,按水平位置排序(升序),每條橫線帶一個標記值,原矩形的左線為1,右線為-1,然后同樣地操作

  • 通過記錄覆蓋的左右端點,以及數量信息,判斷是否覆蓋,掃描一次的同時求出兩線長度。
    • 前面的工作是相同的,把橫線保存在一個表中,按豎直位置排序,然后從下往上掃描所有橫線,在這個方法中,每次掃描一條橫線,都能計算出兩不部分值,一部分是橫線的,一部分是豎線的

      而計算橫線部分的方法和第一種方法是一樣的,即求出【現在這次總區間被覆蓋的長度和上一次總區間被覆蓋的長度的差的絕對值】,另外怎么算豎線部分呢?首先我們要知道添加了這條橫線后會有多少條豎線,答案是2*num,所以為什么要記錄num呢,因為總區間被num條線段覆蓋,那么必定有2*num的端點,這些端點其實就是連著豎線,那么這些豎線的長度是多少呢?

      就是【下一條橫線的高度-現在這條橫線的高度】,只要把這個高度乘上2*num即可

    ?

    我采用的是第二種方法,這里不需要離散化,代碼如下:

    #include <iostream> #include <string> #include <cstring> #include <cstdlib> #include <cstdio> #include <cmath> #include <algorithm> #include <stack> #include <queue> #include <cctype> #include <vector> #include <iterator> #include <set> #include <map> #include <sstream> using namespace std;#define mem(a,b) memset(a,b,sizeof(a)) #define pf printf #define sf scanf #define spf sprintf #define pb push_back #define debug printf("!\n") #define MAXN 20000+5 #define MAX(a,b) a>b?a:b #define blank pf("\n") #define LL long long #define ALL(x) x.begin(),x.end() #define INS(x) inserter(x,x.begin()) #define pqueue priority_queue #define INF 0x3f3f3f3f#define ls (rt<<1) #define rs (rt<<1|1)int n,m;int col[MAXN<<2],len[MAXN<<2],lseg[MAXN<<2],rseg[MAXN<<2],segnum[MAXN<<2];struct node {int l,r,x,c;node(){}node(int a,int b,int c,int d):l(a),r(b),x(c),c(d){}bool operator < (const node &b) const{return x<b.x || x==b.x && c>b.c;} }a[MAXN];void PushUp(int rt,int l,int r) {if(col[rt]){len[rt] = r-l+1;segnum[rt]=2;lseg[rt] = rseg[rt] = 1;}else if(l==r) len[rt] = segnum[rt] = lseg[rt] = rseg[rt] = 0;else{len[rt] = len[ls]+len[rs];segnum[rt] = segnum[ls]+segnum[rs];lseg[rt] = lseg[ls];rseg[rt] = rseg[rs];if(lseg[rs] && rseg[ls]) segnum[rt]-=2;} }void update(int val,int L,int R,int l,int r,int rt) {if(L<=l && r<=R){col[rt] += val;PushUp(rt,l,r);return;}int mid = (l+r)>>1;if(L <= mid) update(val,L,R,l,mid,ls);if(R > mid) update(val,L,R,mid+1,r,rs);PushUp(rt,l,r); }int main() {int i,j,k,t,kase=1;while(~sf("%d",&n) && n){int left = 10000,right = -10000,v=0,ans=0,last=0;mem(len,0);mem(col,0);mem(segnum,0);mem(lseg,0);mem(rseg,0);for(i=0;i<n;i++){int x1,y1,x2,y2;sf("%d%d%d%d",&x1,&y1,&x2,&y2);a[v++] = node(y1,y2,x1,1);a[v++] = node(y1,y2,x2,-1);left = min(left,y1);right = max(right,y2);}//pf("%d %d\n",left,right);sort(a,a+v);for(i=0;i<v;i++){if(a[i].l < a[i].r) update(a[i].c,a[i].l,a[i].r-1,left,right,1);ans+= segnum[1]*(a[i+1].x-a[i].x);ans+= abs(len[1]-last);last = len[1];}pf("%d\n",ans);}return 0; }

    ?

    轉載于:https://www.cnblogs.com/qlky/p/5747719.html

    總結

    以上是生活随笔為你收集整理的ACM-线段树扫描线总结的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。