覆蓋的面積
給定平面上若干矩形,求出被這些矩形覆蓋過至少兩次的區域的面積.
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;
}