1. 程式人生 > >覆蓋的面積

覆蓋的面積

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

 

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

C++版本一

#include <iostream>
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <math.h>
#include <vector>

using namespace std;

const int N=10000+10;
int t,n,cnt=0;
double x,x2,y,y2;
double tree[N],tree2[N];
int flag[N];
double X[N];
void init(){
    memset(tree,0.0,sizeof(tree));
    memset(tree2,0.0,sizeof(tree2));
    memset(flag,0,sizeof(flag));
    cnt=0;
}
struct node{
    double l,r,y;
    int s;
    node(){};
    node(double l0,double r0,double y0,int s0){
        l=l0;
        r=r0;
        y=y0;
        s=s0;
    }
    bool operator <(const node &S) const{
        return y<S.y;
    }
}G[N];
void pushup(int i,int l,int r){

    if(flag[i])
        tree[i]=X[r+1]-X[l];
    else if(l==r)
        tree[i]=0;
    else
        tree[i]=tree[i<<1]+tree[i<<1|1];
    if(flag[i]>1)
        tree2[i]=X[r+1]-X[l];
    else if(l==r)
        tree2[i]=0;
    else if(flag[i]==1)
        tree2[i]=tree[i<<1]+tree[i<<1|1];
    else
        tree2[i]=tree2[i<<1]+tree2[i<<1|1];

}
void updata(int i,int l,int r,int L,int R,int C){
    if(L<=l&&r<=R){
        flag[i]+=C;
        pushup(i,l,r);
        return;
    }
    int m=(l+r)>>1;
    if(R<=m)
        updata(i<<1,l,m,L,R,C);
    else if(m<L)
        updata(i<<1|1,m+1,r,L,R,C);
    else{
        updata(i<<1,l,m,L,m,C);
        updata(i<<1|1,m+1,r,m+1,R,C);
    }
    pushup(i,l,r);
}
int main()
{
    scanf("%d",&t);
    for(int i=1;i<=t;i++){
        init();
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%lf%lf%lf%lf",&x,&y,&x2,&y2);
            G[++cnt]=node(x,x2,y,1);
            X[cnt]=x;
            G[++cnt]=node(x,x2,y2,-1);
            X[cnt]=x2;
        }
        sort(X+1,X+cnt+1);
        sort(G+1,G+cnt+1);
        int k=1;

        for(int i=2;i<=cnt;i++){
            if(X[i]!=X[i+1])    X[++k]=X[i];
        }
        double ans=0;
        for(int i=1;i<cnt;i++){
            int l=lower_bound(X+1,X+k+1,G[i].l)-X;
            int r=lower_bound(X+1,X+k+1,G[i].r)-X-1;
            //cout << tree[1] << endl;
            updata(1,1,k,l,r,G[i].s);
            ans+=tree2[1]*(G[i+1].y-G[i].y);

        }

        printf("%.2f\n",ans+0.0001);

    }
    //cout << "Hello world!" << endl;
    return 0;
}

C++版本二

首先感謝一下 Titanium:http://acm.hdu.edu.cn/showproblem.php?pid=1255 從他的部落格中得到了思路

怎麼計算出重複的面積。

遇到這個求相交矩形的面積的時候,我第一反應就是將cnt標記下推,然後每次都將標記下推, 最後根據cnt的值來模仿1中求面積的方式來求,然後實現起來很複雜,並且估計會超時,所以就百度尋求了一波幫助。

我們先規定sum2 為 至少出現1次時統計的長度 sum為至少出現2次時的長度

如果某個區間的cnt >= 2 那麼 就表示這個這個區間的所有長度都是有效長度, sum就等於這個區間的總長度

當cnt == 1時, 表示這整個區間線段至少出現過一次  並且這個區間內的部分線段可能會出現好多次 

這個時候訪問這個節點的左子樹和右子樹的sum2,sum = sum2(左子樹)+sum2(右子樹)。

因為這個區間的cnt == 1 表示目前這個區間的長度都至少出現過了一次, 由於是區間更新且沒有下推cnt標記

如果左子樹或右子樹上sum2 != 0, 那麼表示在左子樹或右子樹上又出現了一次。

那麼子樹上的一次+目前區間的一次 就能保證出現兩次(及以上)。

(請讀者充分理解線段樹的區域更新, 如果cnt 上有值的話,那麼表示 這個區間被完全覆蓋的。 

如果區間A被完全覆蓋了, 那麼會在區間A對應的cnt++, 然後 進行更新和返回(return;)

但是由於不下推, 所以A的左子樹或者右子樹上的cnt都不會發生變化。所以,如果A的左子樹或右子樹上

的cnt不為0,那麼一定有另一條線掃描到了左子樹或者右子數,並且沒有完全覆蓋區間A。

所以,如果A的cnt == 1並且 A的子樹上有值,那麼子樹上的線段長度和就是至少訪問過2次的線段長度和了);

當然 如果 l==r的時候有效長度還是為0的, 因為葉子節點沒有長度。

https://www.cnblogs.com/MingSD/p/8393274.html

#include<iostream>
#include<algorithm>
#include<iomanip>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
const int N = 1e4;
struct Node
{
    double l, r, h;
    int d;
    bool operator < (const Node & x) const
    {
        return h < x.h;
    }
}A[N];
double X[N], sum[N], sum2[N];
int cnt[N];
void Build(int l, int r, int rt)
{
    sum2[rt] = 0.0,cnt[rt] = 0, sum[rt] = 0.0;
    if(l == r) return ;
    int m = l+r >> 1;
    Build(lson);
    Build(rson);
}
void PushUp(int l, int r, int rt)
{
    if(cnt[rt])
    {
        sum2[rt] = X[r] - X[l-1];
    }
    else if(l == r) sum2[rt] = 0.0;
    else sum2[rt] = sum2[rt<<1] + sum2[rt<<1|1];
    if(cnt[rt] > 1)
        sum[rt] = X[r] - X[l-1];
    else if(l == r) sum[rt] = 0;
    else if(cnt[rt] == 1) sum[rt] = sum2[rt<<1]+sum2[rt<<1|1];
    else sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}
void Revise(int L, int R, int C, int l, int r, int rt)
{
    if(L <= l && r <= R)
    {
        cnt[rt] += C;
        PushUp(l,r,rt);
        return ;
    }
    int m = l+r >> 1;
    if(L <=m) Revise(L,R,C,lson);
    if(m < R) Revise(L,R,C,rson);
    PushUp(l,r,rt);
}
void Add(double l, double r, double h, int d, int i)
{
    A[i].l = l; A[i].h = h;
    A[i].r = r; A[i].d = d;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int T, n;
    cin >> T;
    while(T-- && cin >> n)
    {
        int k = 0;
        double x1, y1, x2, y2;
        for(int i = 1; i <= n; i++)
        {
            cin >> x1 >> y1 >> x2 >> y2;
            Add(x1,x2,y1,1,k);
            X[k++] = x1;
            Add(x1,x2,y2,-1,k);
            X[k++] = x2;
        }
        sort(X,X+k);
        sort(A,A+k);
        int pos = 1;
        for(int i = 1; i < k; i++)
        {
            if(X[i] != X[i-1])
                X[pos++] = X[i];
        }
        Build(1,pos,1);
        double ans = 0;
        for(int i = 0; i < k-1; i++)
        {
            int l = lower_bound(X,X+pos,A[i].l) - X;
            int r = lower_bound(X,X+pos,A[i].r) - X;
            Revise(l+1,r,A[i].d,1,pos,1);
            ans += (A[i+1].h - A[i].h) * sum[1];
        }
        cout << fixed << setprecision(2) << ans+0.0001 << endl;//莫名其妙樣例一的時候沒有四捨五入上去
    }                                  //所以補了一點上去就給過了
    return 0;
}