luogu P3268 [JLOI2016]圆的异或并
https://www.luogu.com.cn/problem/P3268
是一道帮助理解扫描线的好题
首先意识到这题的圆是只有包含和相离两种状态的,所以考虑扫描线
那么圆形怎么扫描线呢?
考虑有一条从左往右的扫描线,一个圆形出现的区间就是圆心的
[
x
i
−
r
i
,
x
i
+
r
i
]
[x_i-r_i,x_i+r_i]
[xi−ri,xi+ri]
然后用呢一个数据结构维护y轴,这里我用set
扫到左端点时候就把这个圆的下端点
y
i
−
r
i
y_i-r_i
yi−ri加入set,扫到右端点的时候就把上端点加入
在加入下端点的时候就找它的前驱
j
j
j如果
j
j
j是上端点说明它们是同层的,否则说明它被
j
j
j包含,然后就可以根据每个圆的层数进行奇加偶减即可
code:
#include<bits/stdc++.h>
#define N 400050
using namespace std;
const double eps = 1e-3;
struct PT {
double x, y, r;
} a[N];
double nowx;
struct cir {
int o, id;
double calc() const {
double dy = sqrt(a[id].r * a[id].r - (nowx - a[id].x) * (nowx - a[id].x));
// printf("*%lf %lf %lf %lf*\n", dy, a[id].r * a[id].r, (nowx), a[id].y + (double) o * (dy + eps));
return a[id].y + (double) o * (dy + eps);
}
bool operator < (const cir & o) const {
return calc() - o.calc() < - eps;
}
};
set<cir> S;
struct SG {
int o;
double x;
int id;
} sg[N << 1];
int cmp(SG x, SG y) {
return x.x < y.x;
}
int n, cnt[N], sz;
int main() {
// freopen("a.in","r",stdin);
scanf("%d", &n);
for(int i = 1; i <= n; i ++) {
scanf("%lf%lf%lf", &a[i].x, &a[i].y, &a[i].r);
sg[++ sz] = (SG){1, a[i].x - a[i].r, i};
sg[++ sz] = (SG){- 1, a[i].x + a[i].r, i};
}
sort(sg + 1, sg + 1 + sz, cmp);
long long ans = 0;
for(int i = 1; i <= sz; i ++) {
nowx = sg[i].x; int id = sg[i].id;
//printf("%lf",nowx);
if(sg[i].o == 1) {
set<cir>::iterator it;
it = S.insert((cir){1, id}).first;//printf("*%d %d %lf\n", id, (*it).id, (*it).calc());
if(it == S.begin()) cnt[id] = 1;
else {
-- it; //printf("*%d %d %lf", id, (*it).id, (*it).calc());
if((*it).o == -1) cnt[id] = cnt[(*it).id] ^ 1;
else cnt[id] = cnt[(*it).id];
}
if(cnt[id]) ans += a[id].r * a[id].r; else ans -= a[id].r * a[id].r;
S.insert((cir){- 1, id});
} else S.erase((cir){1, id}), S.erase((cir){-1, id});
}
// int ss = 0;
// for(int j = 1; j <= n; j ++) ss += cnt[j];//printf("%d ", cnt[j]); printf("\n");
printf("%lld", ans);
return 0;
}