1. 程式人生 > >hdu 1542 Atlantis (線段樹+掃描線)

hdu 1542 Atlantis (線段樹+掃描線)

over down first uniq sed spa 問題 rec style

Atlantis

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 18559 Accepted Submission(s): 7523


Problem Description There are several ancient Greek texts that contain descriptions of the fabled island Atlantis. Some of these texts even include maps of parts of the island. But unfortunately, these maps describe different regions of Atlantis. Your friend Bill has to know the total area for which maps exist. You (unwisely) volunteered to write a program that calculates this quantity.

Input The input file consists of several test cases. Each test case starts with a line containing a single integer n (1<=n<=100) of available maps. The n following lines describe one map each. Each of these lines contains four numbers x1;y1;x2;y2 (0<=x1<x2<=100000;0<=y1<y2<=100000), not necessarily integers. The values (x1; y1) and (x2;y2) are the coordinates of the top-left resp. bottom-right corner of the mapped area.

The input file is terminated by a line containing a single 0. Don’t process it.

Output For each test case, your program should output one section. The first line of each section must be “Test case #k”, where k is the number of the test case (starting with 1). The second one must be “Total explored area: a”, where a is the total explored area (i.e. the area of the union of all rectangles in this test case), printed exact to two digits to the right of the decimal point.

Output a blank line after each test case.

Sample Input 2 10 10 20 20 15 15 25 25.5 0

Sample Output Test case #1 Total explored area: 180.00

題目來源: http://acm.hdu.edu.cn/showproblem.php?pid=1542 題目大意: 再一個平面上給你一些矩形,要你求矩形一共覆蓋的面積。 線段樹掃描線求面積。 這個問題,還是推薦一篇博客,我就只談談對博客的感想: 【hdu1542】線段樹求矩形面積並 - 攔路雨偏似雪花 - 博客園 首先,給的數據是浮點數,一定要離散化,最好要去重。我這裏手寫一份自己常用的適應性很高離散化代碼(去重),大家可以自己拿去調試一下。到本題中還是要有不小的改動,代碼後面也會貼出來,讀者可以對比一下。 技術分享圖片
#include<cstdio>
#include<algorithm>

using namespace std;

const int N=100007;

int t[N],a[N];

int main()
{
      int n;
      scanf("%d",&n);
      for(int i=0;i<n;i++)
      {
            scanf("%d",a+i);
            t[i]=a[i];
      }
      sort(t,t+n);
      int m=unique(t,t+n)-t;//共有每個不重復的元素
      for(int i=0;i<n;i++)
            a[i]=lower_bound(t,t+m,a[i])-t+1;//從1開始編號
      for(int i=0;i<n;i++)
            printf("%d ",a[i]);//測試輸出
      return 0;  
}   
View Code

之前建線段樹都是以某個點為葉子結點的,但是這次卻不能這麽寫,而要以單位長度為葉子結點。

線段樹主要維護cnt(-1表示覆蓋不明確,>=0表示完全覆蓋cnt次,模仿海報問題),len(區間長度,這就要求離散化的時候建立離散後的數值與原數值的映射)。

其他的參考那篇博客就好啦。

技術分享圖片
#include<cstdio>
#include<algorithm>

using namespace std;

double t[410];//排序數組
double a[410];//原始數據
int b[410];//離散後的數據
double c[410];//離散後的數據到原數據的映射

struct tsegment
{
    int x1,x2;
    int y;
    int flag;
};
tsegment segment[210];

int cmp(tsegment a,tsegment b)
{
    return a.y<b.y;
}

struct ttree
{
    int l,r;
    int cover;//-1不確定 >=0完全覆蓋次數 就不打tag了,時間可能會長點
    double len;//區間長度
};
ttree tree[400*4+20];

void pushup(int x)
{
    if(tree[x].l+1==tree[x].r)
        return;
    if(tree[x*2].cover==tree[x*2+1].cover&&tree[x*2].cover>=0)
        tree[x].cover=tree[x*2].cover;
    else
        tree[x].cover=-1;
}

void pushdown(int x)
{
    if(tree[x].l+1==tree[x].r)
        return;
    if(tree[x].cover>=0)
        tree[x*2].cover=tree[x*2+1].cover=tree[x].cover;
}

void build(int x,int l,int r)
{
    tree[x].l=l;
    tree[x].r=r;
    tree[x].len=c[r]-c[l];
    tree[x].cover=0;
    if(l+1<r)
    {
        int mid=(l+r+1)/2;
        build(x*2,l,mid);
        build(x*2+1,mid,r);//葉子節點為一小段區間
    }
}

void modify(int x,int l,int r,int flag)
{
    if(l<=tree[x].l&&r>=tree[x].r&&tree[x].cover>=0)
    {
        tree[x].cover+=flag;
        //printf("%d %d\n",tree[x].l,tree[x].r);
    }
    else if(tree[x].l+1<tree[x].r)
    {
        pushdown(x);
        int mid=(tree[x].l+tree[x].r+1)/2;
        if(l<mid)
            modify(x*2,l,r,flag);
        if(r>mid)
            modify(x*2+1,l,r,flag);
        pushup(x);
    }
}

double query(int x)
{
    if(tree[x].cover>0)
        return tree[x].len;
    else if(tree[x].cover==0)
        return 0;
    else
    {
        pushdown(x);
        double ret=0;
        ret+=query(x*2);
        ret+=query(x*2+1);
        return ret;
    }
}

int main()
{
    int n,k=1;
    while(scanf("%d",&n),n)
    {
        for(int i=0;i<n*4;i++)
        {
            scanf("%lf",a+i);
            t[i]=a[i];
        }
        sort(t,t+n*4);
        int m=unique(t,t+n*4)-t;//共有m個不同的數據
        for(int i=0;i<n*4;i++)
        {
            b[i]=lower_bound(t,t+m,a[i])-t+1;//編號從1開始
            c[b[i]]=a[i];
        }
        //for(int i=1;i<=m;i++)    printf("%f\n",c[i]);

        for(int i=0;i<n;i++)
        {
            int x1=b[i*4],y1=b[i*4+1];
            int x2=b[i*4+2],y2=b[i*4+3];
            segment[i*2].x1=x1;segment[i*2].x2=x2;
            segment[i*2].y=y1;segment[i*2].flag=1;
            segment[i*2+1].x1=x1;segment[i*2+1].x2=x2;
            segment[i*2+1].y=y2;segment[i*2+1].flag=-1;
        }
        sort(segment,segment+n*2,cmp);

        build(1,1,m);
        modify(1,segment[0].x1,segment[0].x2,segment[0].flag);
        double ans=0;
        for(int i=1;i<n*2;i++)
        {
            //printf("%.2f\n",query(1));
            //printf("%d %d\n",segment[i].x1,segment[i].x2);
            ans=ans+query(1)*(c[segment[i].y]-c[segment[i-1].y]);
            modify(1,segment[i].x1,segment[i].x2,segment[i].flag);
        }
        printf("Test case #%d\nTotal explored area: %.2f\n\n",k++,ans);
    }
    return 0;
}
View Code

hdu 1542 Atlantis (線段樹+掃描線)