讲这题之前,我先介绍下线段树扫描线,可能昨晚睡眠不足,导致今天连递归都没看懂,服了.
扫描线只是利用线段树思路解决问题的一种方式而已.
就拿本题来说要你计算图形面积.
这是题目的样例,讲下重点和我对这题的理解,虽然不是很透彻..首先我们可以把x轴按x的值切分,我们要用y轴值进行快速更新.首先这个更新可以用线段树,存tr数组的话考虑4个变量l,r表示当前节点的左右区间,然后用cnt和len分别表示这个矩形在这个区域叠加了几次以及这个区域所占长度,显然因为y是浮点数,不适合我们直接建树,我们需要离散化一下.我们拿build把节点建树,然后我们modify进行更新,更新时候有个pushup操作.等会单独讲,modify就是进行区间更新,和普通的线段树没什么两样,但是这个是区间更新,但是这个是扫描线,有什么特殊的地方呢?就是他增加和减少都是对称的,并且一段>0那么这段长度就是这个区间大小.我们可以直接更新区间即可,因为小的不影响大的正确性。这就是为什么不要lazy标记的原因.而pushup我们怎么更新呢?首先对于cnt>0的那么一定是区间长度.对于=0就有两个,一个是只有一个节点,那么显然是0,不然就是左右儿子的和了.为什么是这个呢?(指左右儿子)因为一旦这个区间里面更新了值,那么它的信息一定会返回到比它大的区间.好了,这题就结束了,我之前不知道为啥被pushup思路卡了,呜呜呜(┯_┯).
代码如下:
#include <bits/stdc++.h> using namespace std; const int N=1e4+5; struct edge{ double x,y1,y2; int k; bool operator <(const edge &t)const { return x<t.x; } }op[N*2]; struct node{ int l,r,cnt; double len; }tr[N*8]; vector<double>v; void pushup(int u) { if(tr[u].cnt) { tr[u].len=v[tr[u].r+1]-v[tr[u].l]; } else if(tr[u].l!=tr[u].r) { tr[u].len=tr[u<<1].len+tr[u<<1|1].len; } else { tr[u].len=0; } } void modify(int u,int l,int r,int k) { if(l<=tr[u].l&&r>=tr[u].r) { tr[u].cnt+=k; pushup(u); return; } else { int mid=tr[u].l+tr[u].r>>1; if(l<=mid) modify(u<<1,l,r,k); if(r>mid) modify(u<<1|1,l,r,k); pushup(u); } } void build(int u,int l,int r) { if(l==r) { tr[u]={l,r,0,0}; return; } else { int mid=l+r>>1; tr[u]={l,r,0,0}; build(u<<1,l,mid); build(u<<1|1,mid+1,r); } } int find(double x)//寻找x的位子 { return lower_bound(v.begin(),v.end(),x)-v.begin(); } int main() { int n,T=1; while(cin>>n,n) { v.clear(); for(int i=0,j=0;i<n;i++) { double x1,y1,x2,y2; scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2); op[j++]={x1,y1,y2,1}; op[j++]={x2,y1,y2,-1}; v.push_back(y1);v.push_back(y2); } sort(v.begin(),v.end()); v.erase(unique(v.begin(),v.end()),v.end()); build(1,0,v.size()-2); sort(op,op+2*n); double ans=0.0; for(int i=0;i<2*n;i++) { if(i>0) ans+=tr[1].len*(op[i].x-op[i-1].x); modify(1,find(op[i].y1),find(op[i].y2)-1,op[i].k); } printf("Test case #%d\n",T++); printf("Total explored area: %.2lf\n",ans); puts(""); } return 0; }