扫描线

转自Mr.Ant的博客
图
四条红线为矩形的上下底边,这里我们称之为扫描线。
如图所示,要求两个矩形的面积并,可以把矩形分成几个小矩形,最后的面积总和为它们的和。
图
对于每个小矩形其 面积S=长*宽。宽就是两条扫描线之间y的差值,这里留给我们的问题就是如何求长了。
因为x是double型而且比较大,所以首先对x进行离散化,x数组下标对应实际的x。
然后就开始建树了,建树比较简单,主要的问题在于如何进行更新树。要注意实际的扫描线长度随时可能变化,并不是相同的,唯一不变的就行要更新数的左右区间(即矩形的底边长度)。
因为扫描线是会变的,所以这里才是我们要重点解决的问题。这里引出我们标记变量cover, 对于每个矩形下底边标记为1,上底边标记为-1。当一根线扫描到下一根线的时候,cover值会覆盖相应的区间,被覆盖的区间flag值会加上此扫描线的cover值。

重点要提的是:覆盖区间只在总区间上变化,总区间一直保持不变。
对扫描线进行y从小到大排序,然后从下往上扫描。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=102;
double sum[N<<3],X[N<<3];
int flag[N<<3];
struct Node{
    double lx,rx,y;
    int s;
    Node(){};
    Node(double lx_, double rx_, double y_, int s_){lx=lx_, rx=rx_, y=y_, s=s_;}
    bool operator <(const Node &S) const{return y<S.y;}
}line[N<<1];
int find(double tmp, int n){
    int l=1,r=n,mid;
    while(l<=r){
        mid=(l+r)>>1;
        if(X[mid]==tmp)return mid;
        else if(X[mid]<tmp)l=mid+1;
        else r=mid-1;
    }
}
inline void push_up(int u, int l, int r){
    if(flag[u])sum[u]=X[r+1]-X[l];
    else if(l==r)sum[u]=0;
    else sum[u]=sum[u<<1]+sum[u<<1|1];
}
void Update(int u, int l, int r, int tl, int tr, int c){
    if(tl<=l&&r<=tr){
        flag[u]+=c,push_up(u,l,r);
        return;
    }
    int mid=(l+r)>>1;
    if(tr<=mid)Update(u<<1,l,mid,tl,tr,c);
    else if(tl>mid)Update(u<<1|1,mid+1,r,tl,tr,c);
    else Update(u<<1,l,mid,tl,tr,c),Update(u<<1|1,mid+1,r,tl,tr,c);
    push_up(u,l,r);
}
int main(){
    int n,tcase=0;
    while(scanf("%d",&n),n){
        int num=0;
        memset(flag,0,sizeof(flag));
        memset(sum,0,sizeof(sum));
        for(int i=1;i<=n;i++){
            double x1,x2,y1,y2;
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            line[++num]=Node(x1,x2,y1,1),X[num]=x1;
            line[++num]=Node(x1,x2,y2,-1),X[num]=x2;
        }
        sort(X+1,X+num+1),sort(line+1,line+num+1);
        int k=1;
        for(int i=2;i<=num;i++)if(X[i]!=X[i+1])X[++k]=X[i];
        double ans=0;
        for(int i=1;i<num;i++){
            int l=find(line[i].lx,k),r=find(line[i].rx,k)-1;
            Update(1,1,k,l,r,line[i].s);
            ans+=sum[1]*(line[i+1].y-line[i].y);
        }
        printf("Test case #%d\n",++tcase);
        printf("Total explored area: %.2lf\n\n",ans);
    }
    return 0;
}

bzoj1645自己码了一遍

#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;
inline char nc(){
	static char buf[100000],*p1=buf,*p2=buf;
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x){
	char c=getchar();x=0;
	for(;!isdigit(c);c=getchar());
	for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
}
typedef long long ll;
const int N=40002;
ll ans;
int n,t[N<<1],tot,h[N],f[N<<3];
struct data{int x,y,h;}a[N];
inline void pushdown(int x){
	f[x<<1]=max(f[x<<1],f[x]),f[x<<1|1]=max(f[x<<1|1],f[x]),f[x]=0;
}
inline void updata(int x,int l,int r,int L,int R,int v){
	if(l>=L&&r<=R){
		f[x]=max(f[x],v);
		return;
	}
	if(f[x])pushdown(x);
	int mid=l+r>>1;
	if(mid>=L)updata(x<<1,l,mid,L,R,v);
	if(mid<R)updata(x<<1|1,mid+1,r,L,R,v);
}
inline int query(int x,int l,int r,int w){
	if(l==r&&l==w)return f[x];
	if(f[x])pushdown(x);
	int mid=l+r>>1;
	if(w<=mid)return query(x<<1,l,mid,w);else return query(x<<1|1,mid+1,r,w);
}
int main(){
	freopen("x.in","r",stdin);
	read(n);
	for(int i=1;i<=n;i++)read(a[i].x),read(a[i].y),read(a[i].h),t[++tot]=a[i].x,t[++tot]=a[i].y;
	sort(t+1,t+tot+1),tot=unique(t+1,t+tot+1)-t-1;
	for(int tp,i=1;i<=n;i++){
		tp=a[i].x,a[i].x=lower_bound(t+1,t+tot+1,tp)-t;
		tp=a[i].y,a[i].y=lower_bound(t+1,t+tot+1,tp)-t-1;
		updata(1,1,tot,a[i].x,a[i].y,a[i].h);
	}
	for(int i=1;i<=tot;i++)ans+=1ll*(t[i+1]-t[i])*query(1,1,tot,i);
	return printf("%lld\n",ans),0;
}
posted @ 2018-11-07 18:52  lnyzo  阅读(109)  评论(0)    收藏  举报