看了一天才明白了
参考博客:https://blog.csdn.net/otowa/article/details/50695401
看完参考博客在看看我对这个的理解。
我的理解:扫描线是一种想法,方便我们去理解,线段的作用:维护我们所需要的值。
比我们要求上图的面积,我们可以将图形看成上图的形式(同种颜色我们求一次面积,最后相加)
我们所说的扫描线其实说的就是平行于X轴的边(看你自己理解,就是看你从下往上扫描,还是从左往右扫描)
那我们取求面积的时候怎么求解呢?
我们可以先把所有的扫描线存下来,然后升序排列 扫描线的参数(l,r,h,flag)
把所有横坐标存下来,然后离散化(因为坐标为double类型,我们建树需要用到int型)
我们求面积 = 相邻扫描线的高度差 (排序后很容易得到) × 扫描线中间有效长度(线段树维护的值,即有颜色的区域长度)
我们怎么用线段树维护我们想要的值呢?
我们用点表示区间来维护长度,可以看下图理解
我们用①表示 1-2之间的有效长度,⑤表示5-6之间的有效长度。
所以我们建树就是上图的表示方式,最终①—⑤就我们所需要的总的有效长度。
我们在更新树的时候只需要pushup就可以了,因为我们只需要①—⑤的有效长度,不需要他的儿子的有效长度
我们在pushup分为三种情况
①:如果这个区间被标记了,并且标记>1,说明这个区间被全部覆盖。
②:这个区间为树的叶子节点,并且没有被标记,说明这个区间没有被覆盖(有效长度为0)
③: 节点不是叶子节点但是没有被标记(可能是完全没有覆盖,可能是被覆盖了一些),他的有效长度为两个儿子的有效长度之和。
模板题:HDU1542
代码:
///#include<bits/stdc++.h>
///#include<unordered_map>
///#include<unordered_set>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<bitset>
#include<set>
#include<stack>
#include<map>
#include<list>
#include<new>
#include<vector>
#define MT(a,b) memset(a,b,sizeof(a));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double pai=acos(-1.0);
const double E=2.718281828459;
const int MAXN=1e5;
const ll mod=1e9+7;
const int INF=0x3f3f3f3f;
int sign=0;
double x[205];
struct node
{
double l;
double r;
double h;
int flag;
bool friend operator<(node a,node b)
{
return a.h<b.h;
}
} line[205];
struct xx
{
int s;
double len;
} root[805];
void pushup(int sign,int l,int r)
{
if(root[sign].s) ///区间被全部覆盖
root[sign].len=x[r+1]-x[l];
else if(l==r) ///没有被覆盖,并且是一个点
root[sign].len=0;
else ///是一条没有整个区间被覆盖的线段,合并左右子的信息
root[sign].len=root[sign<<1].len+root[sign<<1|1].len;
}
void updateroot(int sign,int l,int r,int a,int b,int f)
{
if(l==a&&r==b)
{
root[sign].s+=f;
pushup(sign,l,r);
return ;
}
int mid=(l+r)>>1;
if(b<=mid)
updateroot(sign<<1,l,mid,a,b,f);
else if(a>mid)
updateroot(sign<<1|1,mid+1,r,a,b,f);
else
{
updateroot(sign<<1,l,mid,a,mid,f);
updateroot(sign<<1|1,mid+1,r,mid+1,b,f);
}
pushup(sign,l,r);
}
int main() ///从下往上扫描
{
int n,u=0;
double x1,y1,x2,y2;
while(scanf("%d",&n)!=EOF,n)
{
int sign=0;
memset(root,0,sizeof(root)); ///树清空,也可以自己建一棵空树
for(int i=1; i<=n; i++) ///记录扫描线和横坐标
{
scanf("%lf %lf %lf %lf",&x1,&y1,&x2,&y2);
line[++sign]=node{x1,x2,y1,1};
x[sign]=x1;
line[++sign]=node{x1,x2,y2,-1};
x[sign]=x2;
}
sort(line+1,line+1+sign);
sort(x+1,x+1+sign);
int d=unique(x+1,x+1+sign)-(x+1); ///对横坐标去重,离散化
double ans=0.0;
for(int i=1; i<sign; i++)
{
int a=lower_bound(x+1,x+1+d,line[i].l)-x;
int b=lower_bound(x+1,x+1+d,line[i].r)-x-1; ///因为用点表示区间,矩形的右端点属于r-1的这个矩形的
updateroot(1,1,d-1,a,b,line[i].flag); ///更新,总共d个点,只有d-1个区间
ans+=root[1].len*(line[i+1].h-line[i].h);
}
printf("Test case #%d\n",++u);
printf("Total explored area: %.2f\n\n",ans);
}
return 0;
}