用mark[rt]表示区间内上边和底边的相差个数,hash数组存储经过离散化并且按升序排序的竖边, 用sum[1]表示当前的底边长度。
线段树+扫描线求覆盖面积:
把矩形的竖边(正着看)也就是按x值离散化并按升序排序,横边按y值升序排序,按顺序从最底下的底边开始更新线段树(更新当前底边),如果是底边则把相应区间内的mark值-1,如果是上边则相应区间的mark值+1,当mark为正数时表示相应区间可以完全当作底边,利用线段树更具mark的值更新sum,每次插入一条底边就可以求得对应的一部分面积,最后的和便是整体覆盖面积。
插入竖边按照横边扫也可以。
往线段树插入边1:
红色表示当前底边,绿色表示当前可求面积= sum[1]*(egde[2].h-edge[1].h)。
插入边2:
由于插入的底边,所有底边长度不会减少,当前可求面积=sum[1]*(egde[3].h-edge[2].h)。
插入边3:
当前底边长度增加,当前可求面积=sum[1]*(egde[4].h-edge[3].h)。
插入边4:
由于插入了上边,底边长度变化,也就是通过更新区间改变了sum[1](当前底边长度),当前可求面积=sum[1]*(egde[5].h-edge[4].h)。
mark是不会出现负数的,因为上边和底边是对应的,当一条上边出现时,它前面必定至少出现过一条底边。
#include
#include
#include
using namespace std;
#define maxn 210
double Hash[maxn], sum[maxn<<2];
int mark[maxn<<2];
struct Edge{
double l, r, h;
int c;
Edge(){}
Edge(double x1, double x2, double H, int C):l(x1),r(x2),h(H),c(C){}
bool operator<(const Edge &a)const{
return h>1;
if(a[mid] == key) return mid;
if(a[mid] < key) left = mid + 1;
else right = mid - 1;
}
return -1;
}
void upfather(int n, int left, int right){
if(mark[n]) sum[n] = Hash[right+1] - Hash[left];
else if(left == right) sum[n] = 0;//叶子结点长度为0
else sum[n] = sum[n<<1] + sum[n<<1|1];
}
void update(int L, int R, int rt, int d, int left, int right){
if(L<=left&&right<=R){
mark[rt] += d;
upfather(rt, left, right);
return;
}
int mid = (left+right)>>1;
if(L<=mid) update(L, R, rt<<1, d, left, mid);
if(R>mid) update(L, R, rt<<1|1, d, mid+1, right);
upfather(rt, left, right);
}
int main()
{
int n, i, j, k, m, T = 1;
double x1, x2, y1, y2;
while(~scanf("%d", &n)){
//不用初始化sum和mark,因为扫描完最后一条边后, mark和sum都会变为0
if(!n) continue;
k = 0;
for(i = 0;i < n;i++){
scanf("%lf %lf %lf %lf", &x1, &y1, &x2, &y2);
Hash[k] = x1;
edge[k++] = Edge(x1, x2, y1, 1);
Hash[k] = x2;
edge[k++] = Edge(x1, x2, y2, -1);
}
sort(Hash, Hash+k);
sort(edge, edge+k);
m = 1;
for(i = 1;i < k;i++)
if(Hash[i] != Hash[i-1])
Hash[m++] = Hash[i];
double ans = 0;
for(i = 0;i < k;i++){
int L = Search(edge[i].l, Hash, m);
int R = Search(edge[i].r, Hash, m)-1;
update(L, R, 1, edge[i].c, 0, m-1);
ans += sum[1]*(edge[i+1].h-edge[i].h);
printf("%lf\n", sum[1]);
}
printf("Test case #%d\nTotal explored area: %.2lf\n\n", T++, ans);
}
}
/*
引用自他人:
这里注意下扫描线段时r-1:int R=search(s[i].l,hash,m)-1;
计算底边长时r+1:if(mark[n])sum[n]=hash[right+1]-hash[left];
解释:假设现在有一个线段左端点是l=0,右端点是r=m-1
则我们去更新的时候,会算到sum[1]=hash[mid]-hash[left]+hash[right]-hash[mid+1]
这样的到的底边长sum是错误的,why?因为少算了mid~mid+1的距离,由于我们这利用了
离散化且区间表示线段,所以mid~mid+1之间是有长度的,比如hash[3]=1.2,hash[4]=5.6,mid=3
所以这里用r-1,r+1就很好理解了
*/