Description
二维坐标平面内有n个圆,第i个圆圆心在(Xi,Yi),半径为Ri,权值Vi。任何两个圆都不会相交(也不会相切),但是圆与圆之间可能存在包含关系。当我们在一个圆里面的时候,我们必须经过它的边界一次,才能走出这个圆。
对于不同的两个圆A、B,如果可以从A到B且经过不超过K次边界,则称A、B是连通的。现在的问题是:对于所有的连通的一对圆(A,B),权值差的绝对值最大有多少。多组数据。
T ≤ 10, 2 ≤ n ≤ 50000, 2 ≤ K ≤ 100, 0 < Ri ≤ 200000000, -1000000000 ≤ Xi, Yi, Vi ≤ 1000000000
Solution
如果我们把圆之间的包含关系看做祖先关系的话,那么这n个圆就构成了一棵树(方便起见0为根节点)
然后就变成一道很像点剖的题了。但是发现nk是可以过的,于是我们有一个更简单的做法。
设Fi,j表示与i相距不超过j的点中点权最小值,转移显然。
那么答案就是max(vi-fi,k)了
现在的问题就是怎么建这棵树?即找包含某个圆且半径最小的圆。
考虑从左到右维护扫描线,我们用一个平衡树来维护所有圆和扫描线上交点的大小关系。
那么考虑插入一个圆,如果这个圆上面第一个上交点可以包含它,那么它的父亲就是那个包含它的圆了。因为不可能有比它还小的圆。
如果不包含的话,那么它的父亲就是那个圆的父亲,显然它们是同一层的。
然后就解决了,平衡树的话,随便写一个set就好辣~~
Code
#include <set>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define sqr(x) (x)*(x)
using namespace std;
const int N=5*1e4+5,inf=0x7fffffff;
int n,k,ty,t,tot,cnt,ans,v[N],fa[N],f[N][105],id=1;
struct circle{
int x,y,r,id;
friend bool operator < (circle x,circle y) {
return sqrt(sqr(x.r)-sqr(x.x-t))+x.y>sqrt(sqr(y.r)-sqr(y.x-t))+y.y;
}
}a[N];
struct note{
int x,id,bz;
}p[N*2];
bool cmp(note x,note y) {return x.x<y.x||x.x==y.x&&x.bz<y.bz;}
typedef set<circle> ::iterator it;
set<circle> s;
int main() {
for(scanf("%d",&ty);ty;ty--,id++) {
scanf("%d%d",&n,&k);tot=cnt=0;
fo(i,1,n) scanf("%d%d%d%d",&a[i].x,&a[i].y,&a[i].r,&v[i]),a[i].id=i;
fo(i,1,n) {
p[++tot].x=a[i].x-a[i].r;
p[tot].id=a[i].id;
p[tot].bz=1;
p[++tot].x=a[i].x+a[i].r;
p[tot].id=a[i].id;
p[tot].bz=0;
}
sort(p+1,p+tot+1,cmp);memset(fa,0,sizeof(fa));
fo(i,1,tot) {
t=p[i].x;
if (!p[i].bz) s.erase(s.find(a[p[i].id]));
else {
s.insert(a[p[i].id]);
it l=s.find(a[p[i].id]);
if (l!=s.begin()) {
l--;circle c=*l;
if (c.y-sqrt(sqr(c.r)-sqr(t-c.x))>a[p[i].id].y)
fa[p[i].id]=fa[c.id];
else fa[p[i].id]=c.id;
}
}
}
f[0][0]=inf;fo(i,1,n) f[i][0]=v[i];
fo(j,1,k) {
fo(i,0,n) f[i][j]=f[i][j-1];
fo(i,1,n) {
f[i][j]=min(f[i][j],f[fa[i]][j-1]);
f[fa[i]][j]=min(f[fa[i]][j],f[i][j-1]);
}
}
ans=0;
fo(i,1,n) ans=max(ans,v[i]-f[i][k]);
printf("Case %d: %d\n",id,ans);
}
}