扫描线

落谷 p5490
这里直接讲讲代码的思路:(胡扯下)
这里从左往右扫

先将矩形的竖边放到line结构体中,同时将矩形的两个纵坐标放到v数组中,v排序后,v[1]存第一小y坐标值,v[2]存第二小y坐标值,(这里没有去重,应该是在modify以及pushup中不会有影响),line存的就是每个线段(与y轴平行),按x轴从小到大排列,sgt[1].len表示当前线段所在直线覆盖的矩形的实际长度,然后乘以两个线段间距离,得到扫过矩形面积,(若两线段在同一条直线上),那么说明该次计算矩形面积为0,但是此时的modify会更新sgt[1].len的值,一直到下一条不在同一条直线的线段
这里比较重要的一点是线段树中的cover值,line中的state(1表示矩形的左线段,-1表示矩形的右线段),线段树中某结点的cover值若大于0,说明该结点下的实际长度就为区间长度,若为0,表示该结点覆盖的区间没有被矩形线段完全覆盖,该结点的下的实际长度=左右儿子实际长度之和。
若值为x表示覆盖了x次,而modify中每次更新一个线段(y1-y2),若它的state为1,就会将线段树中对应的区间(y1-y2)的cover都加1;若state为-1,对应区间的cover就-1。而主函数中的sgt[1].len表示当前扫到的地方所覆盖矩形的实际长度 ,这个len会在modify中的pushup中更新

代码:(copy from Agoh)

#include 
#include 

using namespace std;
const int maxn= 2e5+5;
int v[maxn];//存y坐标,从小到大
struct L
{
	int x;
	int y1,y2;
	int state;//判断是左边还是右边
	bool operator<(L oth) {return x<oth.x;}
	
}line[maxn];
struct Node //线段树
{
	int l,r;//区间左右端
	int cover;//覆盖次数
	long long len;//记录区间真实长度	
}sgt[maxn<<3];
inline int ls(int k) {return k<<1;}
inline int rs(int k) {return k<<1|1;}
void pushup(int k)
{
	if(sgt[k].cover) sgt[k].len=sgt[k].r-sgt[k].l;
	else sgt[k].len=sgt[ls(k)].len+sgt[rs(k)].len;
}

void build(int l,int r,int k=1)
{
	sgt[k].l=v[l],sgt[k].r=v[r];
	if(r<=l+1) return ;
	int m=(l+r)>>1;
	build(l,m,ls(k));
	build(m,r,rs(k));
}
void modify(int x,int y,int d,int k=1)
{
	int l=sgt[k].l,r=sgt[k].r;
	if(x<=l&&y>=r)
	{
		sgt[k].cover+=d;
		pushup(k);
		return;
	}
	if(x<sgt[ls(k)].r) modify(x,y,d,ls(k));
	if(y>sgt[rs(k)].l) modify(x,y,d,rs(k));
	pushup(k);
	
	
}



int main()
{
	int a,b,c,d,n;
	cin>> n;
	line[0]=(L){0,0,0,0};
	for(int i=1;i<=n;i++)
	{
		cin>>a>> b >> c >> d;
		v[i]=b,v[n+i]=d;
		line[i]=(L){a,b,d,1},line[n+i]=(L){c,b,d,-1};
	}
	sort(v+1,v+1+(n<<1));
	sort(line+1,line+1+(n<<1));
	build(1,n<<1,1);
	
	unsigned long long ans=0;
	for(int i=1;i<=n<<1;i++)
	{
		ans+=sgt[1].len*(line[i].x-line[i-1].x );
		modify(line[i].y1,line[i].y2,line[i].state,1);//计算好面积后,更新线段的cover,同时将sgt的len更新
	}
	printf("%llu\n",ans);
	
	return 0;
}

hdu 1542 代码都是一样的,就是改下数据类型 代码

你可能感兴趣的:(——计算几何——,扫描线)