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
还是挺好的一道题吧,但是考场上没有做出来啊
什么鬼畜扫描线
首先我们发现如果考虑一下容斥,嵌套起来的圆是加减加减这样的
也就是说一个圆的面积应该加上还是减去只和包住它且最小的圆有关系
然后我们考虑一个圆最左边的一个节点,如果另一个圆包含了这个圆那么在最左边的时候一定存在和满足
然后如果两个圆的关系是相离,一定存在和满足或者
然后我们只需要用一种支持查询前驱后继的数据结构来维护到一个圆的起始位置的时候恰好比它y大的那个圆是什么,用括号序列的形式来理解一下,如果方向相同就是包含,否则就是相离
然后扫描线扫过去
#include<bits/stdc++.h>
using namespace std;
#define N 200010
#define INF 0x3f3f3f3f
#define pi pair<int,int>
struct Circle{int x,y,r;}c[N];
struct Node{int x,id,typ;}p[N<<1];
struct Point{int id,typ;};
long long pow2(int x){return 1ll*x*x;}
int nowx;
bool operator < (const Point &x,const Point &y){
double yx=(double)c[x.id].y+(double)x.typ*(double)sqrt(pow2(c[x.id].r)-pow2(c[x.id].x-nowx));
double yy=(double)c[y.id].y+(double)y.typ*(double)sqrt(pow2(c[y.id].r)-pow2(c[y.id].x-nowx));
if(yx==yy)return x.typ<y.typ;
return yx<yy;
}
bool cmp(Node a,Node b){return a.x<b.x;}
int n,tot=0,vis[N];
set<Point> s;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d%d",&c[i].x,&c[i].y,&c[i].r);
p[++tot]=(Node){c[i].x-c[i].r,i,1};
p[++tot]=(Node){c[i].x+c[i].r,i,-1};
}
sort(p+1,p+tot+1,cmp);
for(int i=1;i<=tot;i++){
nowx=p[i].x;
if(p[i].typ==1){
set<Point>::iterator it;
it=s.upper_bound((Point){p[i].id,1});
if(it==s.end())vis[p[i].id]=1;
else{
if(it->typ==1)vis[p[i].id]=-vis[it->id];
else vis[p[i].id]=vis[it->id];
}
s.insert((Point){p[i].id,1});
s.insert((Point){p[i].id,-1});
}else{
s.erase((Point){p[i].id,1});
s.erase((Point){p[i].id,-1});
}
}
long long ans=0;
for(int i=1;i<=n;i++)ans+=pow2(c[i].r)*vis[i];
printf("%lld",ans);
return 0;
}