您的位置:首页 > 其它

[BZOJ4561][JLoi2016]圆的异或并(扫描线+splay)

2017-04-22 18:48 423 查看

题目描述

传送门

题目大意:在平面直角坐标系中给定N个圆。已知这些圆两两没有交点,即两圆的关系只存在相离和包含。求这些圆的异或面积并。异或面积并为:当一片区域在奇数个圆内则计算其面积,当一片区域在偶数个圆内则不考虑。

题解

这题并没有做出来。。想了很多很多种不科学的方法。。

首先对于一个圆来说,可以将其分成上半个圆弧和下半个圆弧,并且在扫描整个圆的过程中(对于每一个横坐标),两个圆弧的相对位置不变。对于圆之间的相对包含关系,可以搞成一棵树(森林),奇数层的面积加偶数层的面积减就行了。

将每一个圆搞出左边界和右边界,然后按照x坐标排序做扫描线。遇到一个圆的左边界,在splay中插入两个点,分别表示这个圆的上半弧和下半弧。插入的时候需要利用圆的解析式计算一下之前splay中的点在当前横坐标x时的纵坐标y来判断插入位置。

插入之后找一下刚插入的上半弧的前驱。如果前驱是某一个圆的上半弧说明这个圆就是最小的包含当前圆的圆,如果是下半弧说明当前圆和这个圆的被包含关系相同,如果没有前驱则说明这个圆没有被任何圆包含。

遇到圆的右端点把两个圆弧都从splay里删除掉。

这样就做完了

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define N 400005

int n,cnt,root,sz;long long ans;
struct C{int x,y,r;}c
;
struct OPT{int x,id,val;}opt
;
int f
,ch
[2],type
,id_in_graph
,id_in_splay
[2],h
;

int cmp(OPT a,OPT b){return a.x<b.x||(a.x==b.x&&a.val<b.val);}
int get(int x)
{
return ch[f[x]][1]==x;
}
void rotate(int x)
{
int old=f[x],oldf=f[old],wh=get(x);
ch[old][wh]=ch[x][wh^1];
if (ch[old][wh]) f[ch[old][wh]]=old;
ch[x][wh^1]=old;
f[old]=x;
if (oldf) ch[oldf][ch[oldf][1]==old]=x;
f[x]=oldf;
}
void splay(int x)
{
for (int fa;(fa=f[x]);rotate(x))
if (f[fa])
rotate((get(x)==get(fa))?fa:x);
root=x;
}
int compare(int ty,int id,double x,double y)
{
double a=c[id].x,b=c[id].y,r=c[id].r;
double z;
if (!ty) z=sqrt(r*r-(x-a)*(x-a))+b;
else z=-sqrt(r*r-(x-a)*(x-a))+b;
return z>=y;
}
void insert(int id)
{
if (!root)
{
root=++sz;
type[sz]=0;
id_in_splay[id][0]=sz;
id_in_graph[sz]=id;

f[++sz]=sz-1;ch[sz-1][1]=sz;type[sz]=1;
id_in_splay[id][1]=sz;
id_in_graph[sz]=id;
return;
}
int now=root,fa=0,val=c[id].y;
while (1)
{
int wh=compare(type[now],id_in_graph[now],c[id].x-c[id].r,c[id].y);
fa=now;
now=ch[now][wh];
if (!now)
{
ch[fa][wh]=++sz;f[sz]=fa;type[sz]=0;
id_in_splay[id][0]=sz;
id_in_graph[sz]=id;
ch[sz][1]=sz+1;f[sz+1]=sz;type[++sz]=1;
id_in_splay[id][1]=sz;
id_in_graph[sz]=id;
splay(sz-1);
return;
}
}
}
void del(int x)
{
splay(x);int oldroot=root,leftbig;
if (!ch[root][0]&&!ch[root][1])
{
root=0;
return;
}
if (!ch[root][0])
{
root=ch[oldroot][1];
f[root]=0;
return;
}
if (!ch[root][1])
{
root=ch[oldroot][0];
f[root]=0;
return;
}
leftbig=ch[root][0];
while (ch[leftbig][1]) leftbig=ch[leftbig][1];
splay(leftbig);
ch[root][1]=ch[oldroot][1];
f[ch[root][1]]=root;
return;
}
int findpre()
{
int x=ch[root][0];
if (!x) return -1;
while (ch[x][1]) x=ch[x][1];
return x;
}
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);
opt[++cnt].x=c[i].x-c[i].r,opt[cnt].id=i,opt[cnt].val=1;
opt[++cnt].x=c[i].x+c[i].r,opt[cnt].id=i,opt[cnt].val=-1;
}
sort(opt+1,opt+cnt+1,cmp);
for (int i=1;i<=cnt;++i)
{
int id=opt[i].id;
if (opt[i].val==-1)
{
del(id_in_splay[id][0]);
del(id_in_splay[id][1]);
}
else
{
insert(opt[i].id);
int pre=findpre();
if (pre==-1) h[id]=1;
else
{
if (type[pre]==0) h[id]=h[id_in_graph[pre]]+1;
else h[id]=h[id_in_graph[pre]];
}
if (h[id]&1) ans+=(long long)c[id].r*c[id].r;
else ans-=(long long)c[id].r*c[id].r;
}
}
printf("%lld\n",ans);
}


感谢ATP的数据生成器

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<ctime>
#define random(x)(rand()*rand()%x+1)
using namespace std;
int n,X[10010],Y[10010],R[10010];
int getrand(int lim){
return random(lim)-random(lim);
}
double Abs(double x){return (x<0)?-x:x;}
bool check(int x,int y,int r,int id){
double dx=x-X[id],dy=y-Y[id];
dx=sqrt(dx*dx+dy*dy);
if (Abs(r-R[id])<=dx&&Abs(r+R[id])>=dx) return true;
return false;
}
int main()
{
freopen("input.in","w",stdout);
n=100;srand(time(0));
printf("%d\n",n);
for (int i=1;i<=n;){
int x,y,r;
bool flag=false;
x=getrand(100);y=getrand(100);r=random(100);
for (int j=1;j<i;j++)
if (check(x,y,r,j)){
flag=true;break;
}
if (flag==true) continue;
X[i]=x;Y[i]=y;R[i]=r;i++;
}
for (int i=1;i<=n;i++)
printf("%d %d %d\n",X[i],Y[i],R[i]);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: