将所有圆按照 半径 排序, 对第 个圆, 在 中找到一个包含 的圆 , 向 连边, 作为 的父亲,
可以构建出一个森林, 再加一个半径无穷大的圆 , 就是一棵树了 .
进行 树形 , 设 表示 前 个圆, 这个圆不选所能得到的最大值,
, 状态转移复杂度 , 建树复杂度 .
考虑怎么优化 寻找父亲 的过程, 把所有圆分为上下两个圆弧,
上圆弧 坐标取左端点, 下圆弧 坐标取右端点, 按照 坐标进行排序,
顺序加入 std::set<Hu>
中, 以 过当前的 坐标且平行于 轴的直线 与 圆弧的交点 坐标 递增排序,
然后就可以在 中 二分 寻找父亲了, 时间复杂度 .
为了便于理解, 举个例子
#include<bits/stdc++.h>
#define reg register
int read(){
char c;
int s = 0, flag = 1;
while((c=getchar()) && !isdigit(c))
if(c == '-'){ flag = -1, c = getchar(); break ; }
while(isdigit(c)) s = s*10 + c-'0', c = getchar();
return s * flag;
}
const int maxn = 200005;
int N;
int L_x;
int F[maxn];
int Fa[maxn];
struct circle{
int x, y, r, w;
double jdzb(int dir){ return y + dir*sqrt(1.0*r*r - (1.0*L_x-x)*(L_x-x)); }
} A[maxn];
struct Hu{
int dir, id;
bool operator < (const Hu &b) const{ return id==b.id ? dir<b.dir : (A[id].jdzb(dir) < A[b.id].jdzb(b.dir)); }
} B[maxn];
bool cmp(Hu a, Hu b){ return A[a.id].x - a.dir*A[a.id].r < A[b.id].x - b.dir*A[b.id].r; }
std::set <Hu> st;
std::set <Hu>::iterator it;
int main(){
N = read();
int Tmp_1 = 0;
for(reg int i = 1; i <= N; i ++){
A[i].x = read(), A[i].y = read(), A[i].r = read(), A[i].w = read();
B[++ Tmp_1] = (Hu){1, i}, B[++ Tmp_1] = (Hu){-1, i};
}
std::sort(B+1, B+Tmp_1+1, cmp);
for(reg int i = 1; i <= Tmp_1; i ++){
int id = B[i].id, dir = B[i].dir;
L_x = A[id].x - dir*A[id].r;
if(dir == 1){
if(!st.empty()){
it = st.upper_bound(B[i]);
if(it != st.end()) Fa[id] = (it->dir==1)?it->id:Fa[it->id];
}
st.insert((Hu){1, id}), st.insert((Hu){-1, id});
}else{
st.erase((Hu){1, id}), st.erase((Hu){-1, id});
F[Fa[id]] += std::max(F[id], A[id].w);
}
}
printf("%d
", F[0]);
return 0;
}