关于扫描线的存储,离散化就不多说了
#include <cstdio> #include <iostream> #include <algorithm> #include <cmath> #include <string.h> #define lson rt<<1,left,mid #define rson rt<<1|1,mid,right #define eps 1e-8 using namespace std; const int N = 2e3 + 10; const int inf = 0x3f3f3f3f; int n; struct Seg{ double l,r,h; int d; Seg(){} Seg(double l,double r,double h,int d):l(l),r(r),h(h),d(d){} bool operator < (const Seg& rhs) const{ if(h == rhs.h)return d > rhs.d; return h < rhs.h; } }a[N]; int cnt[N << 2];//树的覆盖次数 double all[N<<2];//x轴的离散化
我们要更新维护的是覆盖一次的长度和覆盖两次以上的长度还有覆盖次数cnt
double one[N << 2],two[N << 2];
关键就是pup————更新操作
先来分析,如果这个定点覆盖大于等于两次——》那么one数组和two数组可以直接利用边界更新
如果覆盖一次one数组直接更新,如果是叶子节点two数组清零,非叶子节点two数组为其两个叶子节点的one数组之和————》这里我曾经产生了疑问——》
其实也很好理解——》当前节点被覆盖了一次,覆盖标记我们是没有传递的,所以如果节点的one节点被覆盖(值非零)那么一合计他的值就可以上升为覆盖两次的值了
如果一次都没有覆盖——》叶子结点——》one two清零——》非叶子节点——》向下寻找子节点更新
void pup(int rt,int left,int right) { if(cnt[rt] >= 2)//覆盖超过了两次包括两次直接计算长度 { two[rt] = all[right] - all[left]; one[rt] = two[rt]; } else if(cnt[rt] == 1)//仅仅才覆盖一次 { one[rt] = all[right] - all[left];//计算覆盖一次的长度 if(left + 1 == right) { two[rt] = 0.0;}//如果是叶子节点不必计算两次的长度 else { two[rt] = one[rt<<1] + one[rt<<1|1]; }//向上更新覆盖两次的长度(注意是包含的关系) } else//一次都没有完全覆盖的区间//仅仅考虑从下面向上传递的区间长度 { if(left + 1 == right) { one[rt] = two[rt] = 0.0; } else { one[rt] = one[rt << 1] + one[rt << 1|1]; two[rt] = two[rt<<1] + two[rt<<1|1]; } } }
剩下的代码我就觉得很中规中矩了……
bool Equal(double x,double y) { return abs(x - y) < eps; } void update(double L,double R,int v,int rt,int left,int right) { if(Equal(L,all[left]) && Equal(R,all[right])) { cnt[rt] += v; pup(rt,left,right); //cout<<" "<<L<<" "<<R<<" "<<cnt[rt]<<endl; return; } int mid = (left + right) >> 1; if(left + 1 < right) { if(R <= all[mid] + eps)update(L,R,v,lson); else if(L >= all[mid] - eps)update(L,R,v,rson); else { update(L,all[mid],v,lson); update(all[mid],R,v,rson); } } pup(rt,left,right); } void build(int rt,int left,int right) { one[rt] = 0.0; two[rt] = 0.0; cnt[rt] = 0.0; if(left + 1 == right)return; int mid = (left + right) >> 1; build(lson); build(rson); } int main() { int t; scanf("%d",&t); while(t--){ scanf("%d",&n); for(int i = 0;i < n;i++) { double x1,y1,x2,y2; scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2); a[i] = Seg(x1,x2,y1,1); a[i+n] = Seg(x1,x2,y2,-1); all[i] = x1; all[i+n] = x2; } n <<= 1; sort(a,a+n); sort(all,all+n); int m = unique(all,all+n) - all; // build(1,0,m-1); memset(cnt,0,sizeof(cnt)); memset(one,0.0,sizeof(one)); memset(two,0.0,sizeof(two)) double ans = 0.0; for(int i = 0;i < n-1;++i){ //找到这条线段的区间范围进行更新 //cout<<"now line "<<a[i].l<<" "<<a[i].r<<" "<<a[i].h<<endl; //cout<<"next line "<<a[i+1].l<<" "<<a[i+1].r<<" "<<a[i+1].h<<endl; update(a[i].l,a[i].r,a[i].d,1,0,m-1); //printf("%.2f %.2f",one[1],two[1]); ans += two[1] * (a[i+1].h - a[i].h); } printf("%.2f ",ans); } return 0; }