HDU1255-覆蓋的面積(掃描線求面積並)

覆蓋的面積

Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 6599 Accepted Submission(s): 3366

Problem Description
給定平面上若干矩形,求出被這些矩形覆蓋過至少兩次的區域的面積.

Input
輸入數據的第一行是一個正整數T(1<=T<=100),代表測試數據的數量.每個測試數據的第一行是一個正整數N(1<=N<=1000),代表矩形的數量,然後是N行數據,每一行包含四個浮點數,代表平面上的一個矩形的左上角座標和右下角座標,矩形的上下邊和X軸平行,左右邊和Y軸平行.座標的範圍從0到100000.

注意:本題的輸入數據較多,推薦使用scanf讀入數據.

Output
對於每組測試數據,請計算出被這些矩形覆蓋過至少兩次的區域的面積.結果保留兩位小數.

Sample Input
2
5
1 1 4 2
1 3 3 7
2 1.5 5 4.5
3.5 1.25 7.5 4
6 3 10 7
3
0 0 1 1
1 0 2 1
2 0 3 1

Sample Output
7.63
0.00
題目:HDU1255
題意:中文題意。
思路:才做完裸的掃描線,這道題算是一個小小的拓展吧(不會裸的掃描線戳:HDU1542題解)。
題目要求求矩形覆蓋過至少兩次的區域的面積。最初的想法是在裸的掃描線上記錄覆蓋的次數,配合線段樹的懶惰更新去解題。寫完後,跑完樣例,WA了。。。
後來發現錯誤所在:
樣例1
input
1
4
2 1 3 10
1 2 8 10
4 11 6 12
5 13 7 14
output
8.00
lazy標記會把當前節點的標記先pushdown到兩個子節點,等到下次搜索到子節點時再繼續下傳,對於上面這個樣例,我們先會獲得(2,3)這個x座標集的區間,然後我們去線段樹裏標記(根據區間左開右閉的性質,我們在線段樹裏搜索的區間實際是[2,2],不明白戳HDU1542題解),標記大致草圖如下,在第5行:
這裏寫圖片描述
第二條線我們獲得(1,8)這個x座標集,然後去線段樹裏標記[1,8](實際是[1,7],假裝[1,8]好了),標記位置如圖,在第2行:
這裏寫圖片描述
這時候實際上已經有覆蓋區間了,但是我們從根節點去找時,並沒有發現標記爲2的區間,得到的答案是0,顯然是不對的,也就是說,當我們用懶惰更新先更新小區間,再更新大區間時,就有可能會出現錯誤。
也許是我懶惰更新寫的不對,反正博主最後放棄了這個思路。


廢話說完,現在是正解思路。
對於每個節點,我們記錄兩個值cover(覆蓋長度),line(不考慮覆蓋時,掃描線的長度)。
假設一個區間[l,r]的vis爲1那麼對於[l,r]這個區間來說,它的覆蓋長度cover=leftsong.line+right.line
即當前區間[l,r]存在一條線,這條線必定和當前區間的掃描線相覆蓋。
[l,r]的vis爲2說明當前區間被覆蓋了兩次,那麼cover=posx[r+1]-posx[l] (區間長度)=line.

AC代碼:

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
#include<math.h>
#define met(s,k) memset(s,k,sizeof s)
#define scan(a) scanf("%d",&a)
#define scanl(a) scanf("%lld",&a)
#define scann(a,b) scanf("%d%d",&a,&b)
#define scannl(a,b) scanf("%lld%lld",&a,&b)
#define scannn(a,b,c) scanf("%d%d%d",&a,&b,&c)
#define prin(a) printf("%d\n",a)
#define prinl(a) printf("%lld\n",a)
using namespace std;
typedef long long ll;
const int maxn=1e4+100;
int cont,num;
double posx[maxn];
struct Tree
{
    long double cover,line;
    int vis;
}tree[4*maxn];
struct Edge
{
    double xl,xr,h;
    int flag;
    Edge(){};
    Edge(double a,double b,double c,int d) : xl(a),xr(b),h(c),flag(d){}
    bool operator< (const Edge &a)const{
        return h<a.h;
    }
}edge[maxn];
void pushup(int l,int r,int root)
{
    if(tree[root].vis)tree[root].line=posx[r+1]-posx[l];
    else if(l==r)tree[root].line=0;
    else tree[root].line=tree[root*2].line+tree[root*2+1].line;
    if(tree[root].vis>1)tree[root].cover=tree[root].line;//區間被標記兩次,覆蓋值爲區間的長度
    else if(tree[root].vis)tree[root].cover=tree[root*2].line+tree[root*2+1].line;//區間被標記一次,覆蓋長度等於區間掃描線長度
    else if(l==r)tree[root].cover=0;
    else tree[root].cover=tree[root*2].cover+tree[root*2+1].cover;
}
void update(int nl,int nr,int add,int l,int r,int root)
{
    if(nl==l&&nr==r)
    {
        tree[root].vis+=add;
        pushup(nl,nr,root);
        return ;
    }
    int mid=(l+r)/2;
    if(nr<=mid)update(nl,nr,add,l,mid,root*2);
    else if(nl>mid)update(nl,nr,add,mid+1,r,root*2+1);
    else update(nl,mid,add,l,mid,root*2),update(mid+1,nr,add,mid+1,r,root*2+1);
    pushup(l,r,root);
}
int main()
{
    int n,t;
    scan(t);
    while(t--)
    {
        cont=0;
        num=1;
        scan(n);
        for(int i=0;i<n;i++)
        {
            double x,y,z,m;
            scanf("%lf%lf%lf%lf",&x,&y,&z,&m);
            edge[cont]=Edge(x,z,y,1);
            posx[cont++]=x;
            edge[cont]=Edge(x,z,m,-1);
            posx[cont++]=z;
        }
        sort(posx,posx+cont);
        sort(edge,edge+cont);
        for(int i=1;i<cont;i++)if(posx[i]!=posx[i-1])posx[num++]=posx[i];
        met(tree,0);
        double ans=0;
        for(int i=0;i<cont-1;i++)
        {
            int l=lower_bound(posx,posx+num,edge[i].xl)-posx;
            int r=lower_bound(posx,posx+num,edge[i].xr)-posx-1;
            update(l,r,edge[i].flag,0,num-1,1);
            ans+=tree[1].cover*(edge[i+1].h-edge[i].h);
        }
        printf("%.2f\n",ans);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章