zoukankan      html  css  js  c++  java
  • POJ 1151 & caioj 2058 & CH 0x40数据结构进阶(0x43 线段树)例题4:亚特兰蒂斯

    传送门(欢迎大家来caioj 体验我出的良(er)心数据.)

    扫描线模板题

    因为n很小、横纵坐标又可能是实数,所以我们很自然地想到离散化。

    我们考虑有一条水平线从上到下扫描整个图形。像这样:

    在这里插入图片描述
    downarrow
    在这里插入图片描述
    downarrow
    在这里插入图片描述
    downarrow
    在这里插入图片描述
    downarrow
    在这里插入图片描述
    downarrow
    在这里插入图片描述
    downarrow
    在这里插入图片描述
    downarrow
    在这里插入图片描述
    我们可以发现,直线被截取的长度只有可能在矩形的上下界发生变化.
    我们把横坐标离散化、去重,把区域分成若干份。像这样:

    在这里插入图片描述
    去重后得到8个坐标,可以分出7个块。
    假设纵坐标也这样分。像这样:
    在这里插入图片描述
    这样我们就把图割成了若干块。

    考虑暴力

    因为是从上往下扫,所以我们把上面的边定义为入边,下面的边定义为出边。
    每加入一条入边,对应区域覆盖+1。出边则-1。

    uniqueunique函数

    unique函数的函数原型如下:
    1.只有两个参数,且参数类型都是迭代器:
    iterator unique(iterator it_1,iterator it_2);
    这种类型的unique函数是我们最常用的形式。其中这两个参数表示对容器中[it_1,it_2)范围的元素进行去重(注:区间是前闭后开,即不包含it_2所指的元素),返回值是一个迭代器,它指向的是去重后容器中不重复序列的最后一个元素的下一个元素。

    迭代器又是什么鬼

    迭代器是一个复杂的指针,你把它理解为一个地址就行.

    代码:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N=410;
    struct matrix{double x1,y1,x2,y2;}a[N];
    struct line
    {
    	int x,xx,v;double y;
    	bool operator <(line a)const{return y<a.y;}
    }b[N<<1];//n*2条线段
    double x[N<<1],y[N<<1];int xl,yl;
    int vis[N<<1];//按横坐标分的块被覆盖的次数.
    //扫描线每下移一次(运动到就近的边)就要用被覆盖的区域的长度乘上扫描线运动的长度来更新面积. 
    double ans;
    int main() 
    {
    	int tot=1,n,m;
    	while(scanf("%d",&n),n)
    	{
    		m=n<<1;
    		for(int i=1,j=1;i<=n;i++)
    		{
    			scanf("%lf %lf %lf %lf",&a[i].x1,&a[i].y1,&a[i].x2,&a[i].y2);
    			x[j]=a[i].x1;y[j++]=a[i].y1;
    			x[j]=a[i].x2;y[j++]=a[i].y2;
    		}
    		sort(x+1,x+m+1);sort(y+1,y+m+1);
    		xl=unique(x+1,x+m+1)-(x+1);yl=unique(y+1,y+m+1)-(y+1);
    		for(int i=1,j=1;i<=n;i++)
    		{
    			b[j].x=lower_bound(x+1,x+xl+1,a[i].x1)-x;
    			b[j].xx=lower_bound(x+1,x+xl+1,a[i].x2)-(x+1);//为什么要减一,因为我们在乎的不是点啊.
    			b[j].y=a[i].y2; 
    			b[j++].v=1;//入边 
    			b[j].x=b[j-1].x;
    			b[j].xx=b[j-1].xx;
    			b[j].y=a[i].y1;
    			b[j++].v=-1;//出边 
    		}
    		sort(b+1,b+m+1);//一系列排序复杂度O(n logn) 
    		ans=0;
    		b[0].y=y[1]-1;
    		for(int i=yl,j=m;i;i--)//i为纵坐标标号,j为线段标号
    		{
    			for(int k=1;k<xl;k++)
    			 	if(vis[k])
    				 	ans+=(x[k+1]-x[k])*(y[i+1]-y[i]);
    			while(b[j].y==y[i])
    			{
    				for(int k=b[j].x;k<=b[j].xx;k++)
    					vis[k]+=b[j].v;
    				j--;
    			}
    		}//每条线段枚举一次,一次O(n)——复杂度O(n*n) 
    		printf("Test case #%d
    ",tot++);
    		printf("Total explored area: %.2lf
    
    ",ans);//注意:两个换行
    	}
    	return 0;
    }
    

    正解

    for(int i=yl,j=m;i;i--)//i为纵坐标标号,j为线段标号
    {
    		for(int k=1;k<xl;k++)
    		 	if(vis[k])
    			 	ans+=(x[k+1]-x[k])*(y[i+1]-y[i]);
    		while(b[j].y==y[i])
    		{
    			for(int k=b[j].x;k<=b[j].xx;k++)
    				vis[k]+=b[j].v;
    			j--;
    		}
    	}
    

    我们可以看到上面有区间修改,所以我们可以用线段树来统计吗.
    代码:

    #include<cstdio>
    #include<cctype>
    #include<cstring>
    #include<algorithm>
    #define g getchar()
    #define lc (x<<1)
    #define rc (x<<1|1)
    using namespace std;
    const int N=110;
    
    int n,m,num;
    
    struct line {//线 
    	double x,y,z;
    	int k;
    	bool operator <(line b) const {
    		return x<b.x;
    	}
    }a[N<<1];
    double raw[N<<1];//纵坐标
    
    void disc() {
    	sort(raw+1,raw+n+1);m=1;
    	for(int i=2;i<=n;i++)
    		if(raw[i]!=raw[m])raw[++m]=raw[i];
    }
    
    int val(double x) {
    	int l=1,r=m,mid;
    	while(l<r) {
    		mid=(l+r)>>1;
    		if(raw[mid]<x)l=mid+1;
    		else r=mid;
    	}
    	return l;
    }
    
    struct node {
    	int cnt;//区间覆盖次数
    	double len;
    }tr[N<<3];
    
    void bt(int x,int l,int r) {
    	tr[x].cnt=tr[x].len=0;
    	if(l==r)return ;
    	int mid=(l+r)>>1;
    	bt(lc,l,mid);
    	bt(rc,mid+1,r);
    }
    
    void change(int x,int l,int r,int L,int R,int k) {
    	if(L<=l&&r<=R) { 
    		tr[x].cnt+=k;
    		if(tr[x].cnt) tr[x].len=raw[r+1]-raw[l];
    		else if(l==r) tr[x].len=0;
    		else tr[x].len=tr[lc].len+tr[rc].len;
    		return ;
    	}
    	int mid=(l+r)>>1;
    	if(L<=mid) change(lc,l,mid,L,R,k);
    	if(mid< R) change(rc,mid+1,r,L,R,k);
    	if(!tr[x].cnt) tr[x].len=tr[lc].len+tr[rc].len;
    }
    
    void qr(int &x) {
    	char c=g;x=0;
    	while(!isdigit(c))c=g;
    	while(isdigit(c))x=x*10+c-'0',c=g;
    }
    
    void qr(double &x) {
    	char c=g;x=0;int f=1;
    	while(!isdigit(c)){if(c=='-')f=-1; c=g;}
    	while(isdigit(c))x=x*10+c-'0',c=g;
    	if(c=='.') {
    		double w=0.1; c=g;
    		while(isdigit(c))
    			x+=w*(c-'0'),w/=10,c=g;
    	}	x*=f;
    }
    
    int main() {
    	while(qr(n),n) {
    		for(int i=1,j=2;i<=n;i++,j+=2) {
    			double x,xx,y,z;
    			qr(x);qr(y);qr(xx);qr(z);
    			a[j-1]=(line){x,y,z,1};
    			a[j	 ]=(line){xx,y,z,-1};
    			raw[j-1]=y;
    			raw[j  ]=z;
    		}
    		n <<= 1; disc() ;sort(a+1,a+n+1); 
    		bt(1,1,m-1); double ans=0;
    		for(int i=1;i<n;i++) {
    			int y=val(a[i].y),z=val(a[i].z)-1;
    			change(1,1,m-1,y,z,a[i].k);
    			ans+=tr[1].len*(a[i+1].x-a[i].x);
    		}
    		printf("Test case #%d
    ",++num);
     		printf("Total explored area: %.2lf
    
    ",ans);
     	}
     	return 0;
     }
    
    
  • 相关阅读:
    I Think I Need a Houseboat
    iOS 8 模糊视图(毛玻璃效果)的简单实现UIVisualEffectView
    freemarker报错之二
    [算法]有趣算法合辑[31-40]
    计算机专业术语全称及含义整理
    JAVA经常使用数据结构及原理分析
    我读经典(6):读《文明之光》有感
    流水号的生成(日期+业务码+自增序列)
    桶排序算法
    3.5星|《哈佛商学院最受欢迎的营销课》:跳出营销红海:逆向战略、超越行业和敌意品牌
  • 原文地址:https://www.cnblogs.com/zsyzlzy/p/12373903.html
Copyright © 2011-2022 走看看