首页 > 代码库 > 计数方法(扫描线):JLOI 2016 圆的异或并

计数方法(扫描线):JLOI 2016 圆的异或并

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
  这道题是模板题,经典题。
 1 #include <algorithm>
 2 #include <iostream>
 3 #include <cstring>
 4 #include <cstdio>
 5 #include <cmath>
 6 #include <set>
 7 
 8 #include <cassert>
 9 using namespace std;
10 const int N=200010,M=600010;
11 int n,px[N],py[N],r[N],top,T;
12 long long sqr(long long a){return a*a;}
13 struct Point{
14     int id,x,tp;
15     friend bool operator<(Point x,Point y){
16         double a=py[x.id]+x.tp*sqrt(sqr(r[x.id])-sqr(T-px[x.id]));
17         double b=py[y.id]+y.tp*sqrt(sqr(r[y.id])-sqr(T-px[y.id]));
18         if(a!=b)return a<b;assert(x.id==y.id);return x.tp<y.tp;
19     }
20 }st[M];
21 bool cmp(Point a,Point b){
22     return a.x<b.x;
23 }
24 int res[N];
25 set<Point>s;
26 set<Point>::iterator it;
27 long long ans;
28 
29 
30 int main(){
31     freopen("circle.in","r",stdin);
32     freopen("circle.out","w",stdout);
33     scanf("%d",&n);
34     for(int i=1;i<=n;i++){
35         scanf("%d%d%d",&px[i],&py[i],&r[i]);
36         st[++top]=(Point){i,px[i]-r[i],1};
37         st[++top]=(Point){i,px[i]+r[i],-1};
38     }
39             
40     sort(st+1,st+top+1,cmp);
41     for(int i=1;i<=top;i++){
42         Point x=st[i];T=x.x;
43         if(x.tp==1){
44             it=s.upper_bound((Point){x.id,0,1});
45             if(it==s.end())res[x.id]=1;
46             else{
47                 Point y=*it;
48                 if(y.tp==1)res[x.id]=-res[y.id];
49                 else res[x.id]=res[y.id];
50             }
51             s.insert((Point){x.id,0,-1});
52             s.insert((Point){x.id,0,1});
53         }    
54         else{
55             s.erase((Point){x.id,0,-1});
56             s.erase((Point){x.id,0,1});
57         }
58     }
59     for(int i=1;i<=n;i++)
60         ans+=res[i]*sqr(r[i]);
61     printf("%lld\n",ans);    
62     return 0;
63 }

 

计数方法(扫描线):JLOI 2016 圆的异或并