【BZOJ4561】[JLoi2016]圆的异或并 扫描线

【BZOJ4561】[JLoi2016]圆的异或并

Description

在平面直角坐标系中给定N个圆。已知这些圆两两没有交点,即两圆的关系只存在相离和包含。求这些圆的异或面积并。异或面积并为:当一片区域在奇数个圆内则计算其面积,当一片区域在偶数个圆内则不考虑。

Input

 第一行包含一个正整数N,代表圆的个数。接下来N行,每行3个非负整数x,y,r,表示一个圆心在(x,y),半径为r的圆。保证|x|,|y|,≤10^8,r>0,N<=200000

Output

 仅一行一个整数,表示所有圆的异或面积并除以圆周率Pi的结果。

Sample Input

2
0 0 1
0 0 2

Sample Output

3

题解:首先有一个非常重要的性质,由于所有圆不相交,所以任何时候所有圆的相对位置是不变的。

然后,我们对将个圆拆成加入和删除两个事件,左边加入右边删除。加入时相当于在set中加入了上下两个圆弧。然后用扫描线从左到右扫描,当加入一个圆时,在set中找到它外面的一层圆,则当前圆的符号=-外层圆的符号。特别地,如果我们在当前圆的上面找到了一个下半圆,则说明它和那个圆的关系是并列的,所以符号相同。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <set>
#include <cmath>
using namespace std;
const int maxn=200010;
typedef long long ll;
int n,now;
int x[maxn],y[maxn],r[maxn],f[maxn];
ll ans;
struct edgex
{
	int v,k;
	edgex() {}
	edgex(int a,int b){v=a,k=b;}
	
}p[maxn<<1];
bool operator < (edgex a,edgex b)
{
	int pa=x[a.v]+a.k*r[a.v],pb=x[b.v]+b.k*r[b.v];
	return pa<pb;
}
struct edgey
{
	int v,k;
	edgey() {}
	edgey(int a,int b){v=a,k=b;}
	double gety()
	{
		return y[v]+k*sqrt(1.0*r[v]*r[v]-1.0*(x[v]-now)*(x[v]-now));
	}
};
bool operator < (edgey a,edgey b)
{
	double ya=a.gety(),yb=b.gety();
	if(fabs(ya-yb)<1e-7)	return a.k<b.k;
	return ya<yb;
}
set<edgey> s;
set<edgey>::iterator it;
int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<'0'||gc>'9')	{if(gc=='-')f=-f;	gc=getchar();}
	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
	return ret*f;
}
int main()
{
	n=rd();
	int i;
	for(i=1;i<=n;i++)	x[i]=rd(),y[i]=rd(),r[i]=rd(),p[i]=edgex(i,-1),p[i+n]=edgex(i,1);
	sort(p+1,p+2*n+1);
	for(i=1;i<=2*n;i++)
	{
		if(p[i].k==-1)
		{
			edgey t1(p[i].v,-1),t2(p[i].v,1);
			it=s.upper_bound(t2);
			if(it!=s.end())	f[p[i].v]=-f[(*it).v];
			else	f[p[i].v]=1;
			s.insert(t1),s.insert(t2);
		}
		else	s.erase(edgey(p[i].v,-1)),s.erase(edgey(p[i].v,1));
	}
	for(i=1;i<=n;i++)	ans+=(ll)f[i]*r[i]*r[i];
	printf("%lld",ans);
	return 0;
}

 

原文地址:https://www.cnblogs.com/CQzhangyu/p/7258894.html