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
0 0 1
0 0 2
Sample Output
3
题解:
对于一个圆,拆出它的左端点和右端点.
按这些点的横坐标排序.然后扫这些点.
扫到一个左端点就计算这个圆产生的贡献是正的还是负的.
这个可以每次找第一个包含这个圆的圆或者和它并列的圆.
用set维护一下当前所有圆在这个左端点的横坐标上的纵坐标就可以实现.
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<set>
#include<cmath>
#include<algorithm>
#define N 200010
#define ll long long
using namespace std;
struct cir{ll x,y,r;}c[N];
struct use{int p,x,k;}q[N<<1];
int n;
set<use>s;
ll temp,g[N],ans;
ll cal(ll x){return x*x;}
bool cmp(use a,use b){return a.x<b.x;}
bool operator<(use a,use b){
double x=c[a.p].y+a.k*sqrt(cal(c[a.p].r)-cal(temp-c[a.p].x));
double y=c[b.p].y+b.k*sqrt(cal(c[b.p].r)-cal(temp-c[b.p].x));
if (x!=y) return x<y;
else return a.k<b.k;
}
int main(){
scanf("%d",&n);
for (int i=1;i<=n;i++){
scanf("%lld%lld%lld",&c[i].x,&c[i].y,&c[i].r);
q[(i<<1)-1]=use{i,c[i].x-c[i].r,1};
q[i<<1]={i,c[i].x+c[i].r,-1};
}
sort(q+1,q+(n<<1)+1,cmp);
for (int i=1;i<=(n<<1);i++){
temp=q[i].x;
if (q[i].k==1){
set<use>::iterator it;
it=s.upper_bound(use{q[i].p,0,-1});
if (it==s.end()) g[q[i].p]=1;
else{
if ((*it).k==-1) g[q[i].p]=g[(*it).p];
else g[q[i].p]=-g[(*it).p];
}
s.insert(use{q[i].p,0,1});s.insert(use{q[i].p,0,-1});
}
else{
s.erase(use{q[i].p,0,1});s.erase(use{q[i].p,0,-1});
}
}
for (int i=1;i<=n;i++)
ans+=cal(c[i].r)*g[i];
cout<<ans<<endl;
}