BZOJ4561 JLoi2016 圆的异或并 【扫描线】【set】*

c++ 同时被 3 个专栏收录
177 篇文章 0 订阅
114 篇文章 0 订阅
1 篇文章 0 订阅

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


还是挺好的一道题吧,但是考场上没有做出来啊

什么鬼畜扫描线


首先我们发现如果考虑一下容斥,嵌套起来的圆是加减加减这样的
也就是说一个圆的面积应该加上还是减去只和包住它且最小的圆有关系

然后我们考虑一个圆最左边的一个节点 (x,y) ( x , y ) ,如果另一个圆包含了这个圆那么在最左边的时候一定存在 (x,y1) ( x , y 1 ) (x,y2) ( x , y 2 ) 满足 y1<y<y2 y 1 < y < y 2
然后如果两个圆的关系是相离,一定存在 (x,y1) ( x , y 1 ) (x,y2) ( x , y 2 ) 满足 y1<=y2<y y 1 <= y 2 < y 或者 y<y1<=y2 y < y 1 <= y 2
然后我们只需要用一种支持查询前驱后继的数据结构来维护到一个圆的起始位置的时候恰好比它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;
}
  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值