这题现场的时候n=30000部分分居然是给暴力的,我分段写的个乱搞40分结果暴力都60分,差评
正解:
首先可知一个圆被奇数个圆套则答案减去其面积,被偶数个套则加上其面积,然后我们维护一个垂直于x轴扫描线,从左向右扫,每个圆拆成加入和删除两个事件,由于圆和圆不相交,所以一个圆可以看成一个括号,整个扫描线上是一个括号序列,而且随扫描线当前x增加括号之间相对顺序不变(扫描线都是某些相对顺序不变,然后维护当前x?)
因为圆和圆不相交,所以加入和删除的时候肯定都是对最内层的括号操作,只会影响到自己,所以我们只需要对扫描线上每个点记录他属于哪个圆(算出当前x下y坐标),是左括号还是右括号和外边套了几层(在新加入括号的时候用这两个算出新加的括号是第几层),然后在加入一对括号的时候查询他外面套了几层更新答案即可
#include<iostream> #include<cstdlib> #include<cstdio> #include<cstring> #include<cmath> #include<ctime> #include<algorithm> #include<iomanip> #include<vector> #include<stack> #include<queue> #include<map> #include<set> #include<bitset> using namespace std; #define MAXN 200010 #define MAXM 1010 #define ll long long #define INF 1000000000 #define MOD 1000000007 #define eps 1e-8 double nowx; int X[MAXN],Y[MAXN],R[MAXN]; struct cir{ bool ud; int bel; int c; cir(){ } cir(int _ud,int _bel,int _c=0){ ud=_ud; bel=_bel; c=_c; } inline double y(int x){ double re=Y[bel]; if(ud){ re+=sqrt(1.0*R[bel]*R[bel]-1.0*(x-X[bel])*(x-X[bel])); }else{ re-=sqrt(1.0*R[bel]*R[bel]-1.0*(x-X[bel])*(x-X[bel])); } return re; } friend bool operator <(cir x,cir y){ return fabs(x.y(nowx)-y.y(nowx))>eps?x.y(nowx)>y.y(nowx):x.ud>y.ud; } }; struct evt{ int x; int v; friend bool operator <(evt x,evt y){ return x.x<y.x; } }; int n; set<cir>wzh; evt e[MAXN*2]; int tot; ll ans; int main(){ int i; scanf("%d",&n); for(i=1;i<=n;i++){ scanf("%d%d%d",&X[i],&Y[i],&R[i]); e[++tot].x=X[i]-R[i]; e[tot].v=i; e[++tot].x=X[i]+R[i]; e[tot].v=-i; } R[0]=INF; nowx=-INF; wzh.insert(cir(1,0,-1)); wzh.insert(cir(0,0,-1)); sort(e+1,e+tot+1); int c; for(i=1;i<=tot;i++){ nowx=e[i].x; if(e[i].v>0){ cir t=*wzh.upper_bound(cir(0,e[i].v)); if(t.ud){ c=t.c; }else{ c=t.c+1; } if(c&1){ ans-=(ll)R[e[i].v]*R[e[i].v]; }else{ ans+=(ll)R[e[i].v]*R[e[i].v]; } wzh.insert(cir(1,e[i].v,c)); wzh.insert(cir(0,e[i].v,c)); }else{ e[i].v=-e[i].v; wzh.erase(cir(1,e[i].v,c)); wzh.erase(cir(0,e[i].v,c)); } } printf("%lld\n",ans); return 0; } /* */